diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime')
8 files changed, 6365 insertions, 0 deletions
| diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp new file mode 100644 index 000000000000..60549663db66 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.cpp @@ -0,0 +1,191 @@ +//===-- RenderScriptExpressionOpts.cpp --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <string> + +#include "llvm/ADT/None.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +#include "clang/Basic/TargetOptions.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Log.h" + +#include "RenderScriptExpressionOpts.h" +#include "RenderScriptRuntime.h" +#include "RenderScriptx86ABIFixups.h" + +using namespace lldb_private; +using namespace lldb_renderscript; + +// [``slang``](https://android.googlesource.com/platform/frameworks/compile/slang), +// the compiler frontend for RenderScript embeds an ARM specific triple in IR +// that is shipped in the app, after generating IR that has some assumptions +// that an ARM device is the target. As the IR is then compiled on a device of +// unknown (at time the IR was generated at least) architecture, when calling +// RenderScript API function as part of debugger expressions, we have to +// perform a fixup pass that removes those assumptions right before the module +// is sent to be generated by the llvm backend. + +namespace { +bool registerRSDefaultTargetOpts(clang::TargetOptions &proto, +                                 const llvm::Triple::ArchType &arch) { +  switch (arch) { +  case llvm::Triple::ArchType::x86: +    proto.Triple = "i686--linux-android"; +    proto.CPU = "atom"; +    proto.Features.push_back("+long64"); +    // Fallthrough for common x86 family features +    LLVM_FALLTHROUGH; +  case llvm::Triple::ArchType::x86_64: +    proto.Features.push_back("+mmx"); +    proto.Features.push_back("+sse"); +    proto.Features.push_back("+sse2"); +    proto.Features.push_back("+sse3"); +    proto.Features.push_back("+ssse3"); +    proto.Features.push_back("+sse4.1"); +    proto.Features.push_back("+sse4.2"); +    break; +  case llvm::Triple::ArchType::mipsel: +    // pretend this is `arm' for the front-end +    proto.Triple = "armv7-none-linux-android"; +    proto.CPU = ""; +    proto.Features.push_back("+long64"); +    break; +  case llvm::Triple::ArchType::mips64el: +    // pretend this is `aarch64' for the front-end +    proto.Triple = "aarch64-none-linux-android"; +    proto.CPU = ""; +    break; +  default: +    return false; +  } +  return true; +} +} // end anonymous namespace + +bool RenderScriptRuntimeModulePass::runOnModule(llvm::Module &module) { +  bool changed_module = false; +  Log *log( +      GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS)); + +  std::string err; +  llvm::StringRef real_triple = +      m_process_ptr->GetTarget().GetArchitecture().GetTriple().getTriple(); +  const llvm::Target *target_info = +      llvm::TargetRegistry::lookupTarget(real_triple, err); +  if (!target_info) { +    if (log) +      log->Warning("couldn't determine real target architecture: '%s'", +                   err.c_str()); +    return false; +  } + +  llvm::Optional<llvm::Reloc::Model> reloc_model = llvm::None; +  assert(m_process_ptr && "no available lldb process"); +  switch (m_process_ptr->GetTarget().GetArchitecture().GetMachine()) { +  case llvm::Triple::ArchType::x86: +    changed_module |= fixupX86FunctionCalls(module); +    // For some reason this triple gets totally missed by the backend, and must +    // be set manually. There a reference in bcc/Main.cpp about auto feature- +    // detection being removed from LLVM3.5, but I can't see that discussion +    // anywhere public. +    real_triple = "i686--linux-android"; +    break; +  case llvm::Triple::ArchType::x86_64: +    changed_module |= fixupX86_64FunctionCalls(module); +    break; +  case llvm::Triple::ArchType::mipsel: +  case llvm::Triple::ArchType::mips64el: +    // No actual IR fixup pass is needed on MIPS, but the datalayout and +    // targetmachine do need to be explicitly set. + +    // bcc explicitly compiles MIPS code to use the static relocation model due +    // to an issue with relocations in mclinker. see +    // libbcc/support/CompilerConfig.cpp for details +    reloc_model = llvm::Reloc::Static; +    changed_module = true; +    break; +  case llvm::Triple::ArchType::arm: +  case llvm::Triple::ArchType::aarch64: +    // ARM subtargets need no fixup passes as they are the initial target as +    // generated by the +    // slang compiler frontend. +    break; +  default: +    if (log) +      log->Warning("Ignoring unknown renderscript target"); +    return false; +  } + +  if (changed_module) { +    llvm::TargetOptions options; +    llvm::TargetMachine *target_machine = target_info->createTargetMachine( +        real_triple, "", "", options, reloc_model); +    assert(target_machine && +           "failed to identify RenderScriptRuntime target machine"); +    // We've been using a triple and datalayout of some ARM variant all along, +    // so we need to let the backend know that this is no longer the case. +    if (log) { +      log->Printf("%s - Changing RS target triple to '%s'", __FUNCTION__, +                  real_triple.str().c_str()); +      log->Printf( +          "%s - Changing RS datalayout to '%s'", __FUNCTION__, +          target_machine->createDataLayout().getStringRepresentation().c_str()); +    } +    module.setTargetTriple(real_triple); +    module.setDataLayout(target_machine->createDataLayout()); +  } +  return changed_module; +} + +char RenderScriptRuntimeModulePass::ID = 0; + +namespace lldb_private { + +bool RenderScriptRuntime::GetOverrideExprOptions(clang::TargetOptions &proto) { +  auto *process = GetProcess(); +  assert(process); +  return registerRSDefaultTargetOpts( +      proto, process->GetTarget().GetArchitecture().GetMachine()); +} + +bool RenderScriptRuntime::GetIRPasses(LLVMUserExpression::IRPasses &passes) { +  if (!m_ir_passes) +    m_ir_passes = new RSIRPasses(GetProcess()); +  assert(m_ir_passes); + +  passes.EarlyPasses = m_ir_passes->EarlyPasses; +  passes.LatePasses = m_ir_passes->LatePasses; + +  return true; +} + +namespace lldb_renderscript { + +RSIRPasses::RSIRPasses(Process *process) { +  IRPasses(); +  assert(process); + +  EarlyPasses = std::make_shared<llvm::legacy::PassManager>(); +  assert(EarlyPasses); +  EarlyPasses->add(new RenderScriptRuntimeModulePass(process)); +} + +RSIRPasses::~RSIRPasses() {} + +} // namespace lldb_renderscript +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h new file mode 100644 index 000000000000..3ec4e37b6db0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptExpressionOpts.h @@ -0,0 +1,52 @@ +//===-- RenderScriptExpressionOpts.h ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_RENDERSCRIPT_EXPROPTS_H +#define LLDB_RENDERSCRIPT_EXPROPTS_H + +#include "llvm/IR/Module.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/lldb-private.h" + +#include "RenderScriptRuntime.h" +#include "RenderScriptx86ABIFixups.h" + +// RenderScriptRuntimeModulePass is a simple llvm::ModulesPass that is used +// during expression evaluation to apply RenderScript-specific fixes for +// expression evaluation. In particular this is used to make expression IR +// conformant with the ABI generated by the slang frontend. This ModulePass is +// executed in ClangExpressionParser::PrepareForExecution whenever an +// expression's DWARF language is eLanguageTypeExtRenderscript + +class RenderScriptRuntimeModulePass : public llvm::ModulePass { +public: +  static char ID; +  RenderScriptRuntimeModulePass(const lldb_private::Process *process) +      : ModulePass(ID), m_process_ptr(process) {} + +  bool runOnModule(llvm::Module &module) override; + +private: +  const lldb_private::Process *m_process_ptr; +}; + +namespace lldb_private { +namespace lldb_renderscript { +struct RSIRPasses : public lldb_private::LLVMUserExpression::IRPasses { +  RSIRPasses(lldb_private::Process *process); + +  ~RSIRPasses(); +}; +} // namespace lldb_renderscript +} // namespace lldb_private +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp new file mode 100644 index 000000000000..c9cd34cf379d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp @@ -0,0 +1,5055 @@ +//===-- RenderScriptRuntime.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RenderScriptRuntime.h" +#include "RenderScriptScriptGroup.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/DumpDataExtractor.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/DataFormatters/DumpValueObjectOptions.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Status.h" + +#include "llvm/ADT/StringSwitch.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_renderscript; + +#define FMT_COORD "(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ")" + +char RenderScriptRuntime::ID = 0; + +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; +}; + +// ArgItem is used by the GetArgs() function when reading function arguments +// from the target. +struct ArgItem { +  enum { ePointer, eInt32, eInt64, eLong, eBool } type; + +  uint64_t value; + +  explicit operator uint64_t() const { return value; } +}; + +// Context structure to be passed into GetArgsXXX(), argument reading functions +// below. +struct GetArgsCtx { +  RegisterContext *reg_ctx; +  Process *process; +}; + +bool GetArgsX86(const GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) { +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + +  Status err; + +  // get the current stack pointer +  uint64_t sp = ctx.reg_ctx->GetSP(); + +  for (size_t i = 0; i < num_args; ++i) { +    ArgItem &arg = arg_list[i]; +    // advance up the stack by one argument +    sp += sizeof(uint32_t); +    // get the argument type size +    size_t arg_size = sizeof(uint32_t); +    // read the argument from memory +    arg.value = 0; +    Status err; +    size_t read = +        ctx.process->ReadMemory(sp, &arg.value, sizeof(uint32_t), err); +    if (read != arg_size || !err.Success()) { +      if (log) +        log->Printf("%s - error reading argument: %" PRIu64 " '%s'", +                    __FUNCTION__, uint64_t(i), err.AsCString()); +      return false; +    } +  } +  return true; +} + +bool GetArgsX86_64(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) { +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + +  // number of arguments passed in registers +  static const uint32_t args_in_reg = 6; +  // register passing order +  static const std::array<const char *, args_in_reg> reg_names{ +      {"rdi", "rsi", "rdx", "rcx", "r8", "r9"}}; +  // argument type to size mapping +  static const std::array<size_t, 5> arg_size{{ +      8, // ePointer, +      4, // eInt32, +      8, // eInt64, +      8, // eLong, +      4, // eBool, +  }}; + +  Status err; + +  // get the current stack pointer +  uint64_t sp = ctx.reg_ctx->GetSP(); +  // step over the return address +  sp += sizeof(uint64_t); + +  // check the stack alignment was correct (16 byte aligned) +  if ((sp & 0xf) != 0x0) { +    if (log) +      log->Printf("%s - stack misaligned", __FUNCTION__); +    return false; +  } + +  // find the start of arguments on the stack +  uint64_t sp_offset = 0; +  for (uint32_t i = args_in_reg; i < num_args; ++i) { +    sp_offset += arg_size[arg_list[i].type]; +  } +  // round up to multiple of 16 +  sp_offset = (sp_offset + 0xf) & 0xf; +  sp += sp_offset; + +  for (size_t i = 0; i < num_args; ++i) { +    bool success = false; +    ArgItem &arg = arg_list[i]; +    // arguments passed in registers +    if (i < args_in_reg) { +      const RegisterInfo *reg = +          ctx.reg_ctx->GetRegisterInfoByName(reg_names[i]); +      RegisterValue reg_val; +      if (ctx.reg_ctx->ReadRegister(reg, reg_val)) +        arg.value = reg_val.GetAsUInt64(0, &success); +    } +    // arguments passed on the stack +    else { +      // get the argument type size +      const size_t size = arg_size[arg_list[i].type]; +      // read the argument from memory +      arg.value = 0; +      // note: due to little endian layout reading 4 or 8 bytes will give the +      // correct value. +      size_t read = ctx.process->ReadMemory(sp, &arg.value, size, err); +      success = (err.Success() && read == size); +      // advance past this argument +      sp -= size; +    } +    // fail if we couldn't read this argument +    if (!success) { +      if (log) +        log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s", +                    __FUNCTION__, uint64_t(i), err.AsCString("n/a")); +      return false; +    } +  } +  return true; +} + +bool GetArgsArm(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) { +  // number of arguments passed in registers +  static const uint32_t args_in_reg = 4; + +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + +  Status err; + +  // get the current stack pointer +  uint64_t sp = ctx.reg_ctx->GetSP(); + +  for (size_t i = 0; i < num_args; ++i) { +    bool success = false; +    ArgItem &arg = arg_list[i]; +    // arguments passed in registers +    if (i < args_in_reg) { +      const RegisterInfo *reg = ctx.reg_ctx->GetRegisterInfoAtIndex(i); +      RegisterValue reg_val; +      if (ctx.reg_ctx->ReadRegister(reg, reg_val)) +        arg.value = reg_val.GetAsUInt32(0, &success); +    } +    // arguments passed on the stack +    else { +      // get the argument type size +      const size_t arg_size = sizeof(uint32_t); +      // clear all 64bits +      arg.value = 0; +      // read this argument from memory +      size_t bytes_read = +          ctx.process->ReadMemory(sp, &arg.value, arg_size, err); +      success = (err.Success() && bytes_read == arg_size); +      // advance the stack pointer +      sp += sizeof(uint32_t); +    } +    // fail if we couldn't read this argument +    if (!success) { +      if (log) +        log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s", +                    __FUNCTION__, uint64_t(i), err.AsCString("n/a")); +      return false; +    } +  } +  return true; +} + +bool GetArgsAarch64(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) { +  // number of arguments passed in registers +  static const uint32_t args_in_reg = 8; + +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + +  for (size_t i = 0; i < num_args; ++i) { +    bool success = false; +    ArgItem &arg = arg_list[i]; +    // arguments passed in registers +    if (i < args_in_reg) { +      const RegisterInfo *reg = ctx.reg_ctx->GetRegisterInfoAtIndex(i); +      RegisterValue reg_val; +      if (ctx.reg_ctx->ReadRegister(reg, reg_val)) +        arg.value = reg_val.GetAsUInt64(0, &success); +    } +    // arguments passed on the stack +    else { +      if (log) +        log->Printf("%s - reading arguments spilled to stack not implemented", +                    __FUNCTION__); +    } +    // fail if we couldn't read this argument +    if (!success) { +      if (log) +        log->Printf("%s - error reading argument: %" PRIu64, __FUNCTION__, +                    uint64_t(i)); +      return false; +    } +  } +  return true; +} + +bool GetArgsMipsel(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) { +  // number of arguments passed in registers +  static const uint32_t args_in_reg = 4; +  // register file offset to first argument +  static const uint32_t reg_offset = 4; + +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + +  Status err; + +  // find offset to arguments on the stack (+16 to skip over a0-a3 shadow +  // space) +  uint64_t sp = ctx.reg_ctx->GetSP() + 16; + +  for (size_t i = 0; i < num_args; ++i) { +    bool success = false; +    ArgItem &arg = arg_list[i]; +    // arguments passed in registers +    if (i < args_in_reg) { +      const RegisterInfo *reg = +          ctx.reg_ctx->GetRegisterInfoAtIndex(i + reg_offset); +      RegisterValue reg_val; +      if (ctx.reg_ctx->ReadRegister(reg, reg_val)) +        arg.value = reg_val.GetAsUInt64(0, &success); +    } +    // arguments passed on the stack +    else { +      const size_t arg_size = sizeof(uint32_t); +      arg.value = 0; +      size_t bytes_read = +          ctx.process->ReadMemory(sp, &arg.value, arg_size, err); +      success = (err.Success() && bytes_read == arg_size); +      // advance the stack pointer +      sp += arg_size; +    } +    // fail if we couldn't read this argument +    if (!success) { +      if (log) +        log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s", +                    __FUNCTION__, uint64_t(i), err.AsCString("n/a")); +      return false; +    } +  } +  return true; +} + +bool GetArgsMips64el(GetArgsCtx &ctx, ArgItem *arg_list, size_t num_args) { +  // number of arguments passed in registers +  static const uint32_t args_in_reg = 8; +  // register file offset to first argument +  static const uint32_t reg_offset = 4; + +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + +  Status err; + +  // get the current stack pointer +  uint64_t sp = ctx.reg_ctx->GetSP(); + +  for (size_t i = 0; i < num_args; ++i) { +    bool success = false; +    ArgItem &arg = arg_list[i]; +    // arguments passed in registers +    if (i < args_in_reg) { +      const RegisterInfo *reg = +          ctx.reg_ctx->GetRegisterInfoAtIndex(i + reg_offset); +      RegisterValue reg_val; +      if (ctx.reg_ctx->ReadRegister(reg, reg_val)) +        arg.value = reg_val.GetAsUInt64(0, &success); +    } +    // arguments passed on the stack +    else { +      // get the argument type size +      const size_t arg_size = sizeof(uint64_t); +      // clear all 64bits +      arg.value = 0; +      // read this argument from memory +      size_t bytes_read = +          ctx.process->ReadMemory(sp, &arg.value, arg_size, err); +      success = (err.Success() && bytes_read == arg_size); +      // advance the stack pointer +      sp += arg_size; +    } +    // fail if we couldn't read this argument +    if (!success) { +      if (log) +        log->Printf("%s - error reading argument: %" PRIu64 ", reason: %s", +                    __FUNCTION__, uint64_t(i), err.AsCString("n/a")); +      return false; +    } +  } +  return true; +} + +bool GetArgs(ExecutionContext &exe_ctx, ArgItem *arg_list, size_t num_args) { +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + +  // verify that we have a target +  if (!exe_ctx.GetTargetPtr()) { +    if (log) +      log->Printf("%s - invalid target", __FUNCTION__); +    return false; +  } + +  GetArgsCtx ctx = {exe_ctx.GetRegisterContext(), exe_ctx.GetProcessPtr()}; +  assert(ctx.reg_ctx && ctx.process); + +  // dispatch based on architecture +  switch (exe_ctx.GetTargetPtr()->GetArchitecture().GetMachine()) { +  case llvm::Triple::ArchType::x86: +    return GetArgsX86(ctx, arg_list, num_args); + +  case llvm::Triple::ArchType::x86_64: +    return GetArgsX86_64(ctx, arg_list, num_args); + +  case llvm::Triple::ArchType::arm: +    return GetArgsArm(ctx, arg_list, num_args); + +  case llvm::Triple::ArchType::aarch64: +    return GetArgsAarch64(ctx, arg_list, num_args); + +  case llvm::Triple::ArchType::mipsel: +    return GetArgsMipsel(ctx, arg_list, num_args); + +  case llvm::Triple::ArchType::mips64el: +    return GetArgsMips64el(ctx, arg_list, num_args); + +  default: +    // unsupported architecture +    if (log) { +      log->Printf( +          "%s - architecture not supported: '%s'", __FUNCTION__, +          exe_ctx.GetTargetRef().GetArchitecture().GetArchitectureName()); +    } +    return false; +  } +} + +bool IsRenderScriptScriptModule(ModuleSP module) { +  if (!module) +    return false; +  return module->FindFirstSymbolWithNameAndType(ConstString(".rs.info"), +                                                eSymbolTypeData) != nullptr; +} + +bool ParseCoordinate(llvm::StringRef coord_s, RSCoordinate &coord) { +  // takes an argument of the form 'num[,num][,num]'. Where 'coord_s' is a +  // comma separated 1,2 or 3-dimensional coordinate with the whitespace +  // trimmed. Missing coordinates are defaulted to zero. If parsing of any +  // elements fails the contents of &coord are undefined and `false` is +  // returned, `true` otherwise + +  RegularExpression regex; +  RegularExpression::Match regex_match(3); + +  bool matched = false; +  if (regex.Compile(llvm::StringRef("^([0-9]+),([0-9]+),([0-9]+)$")) && +      regex.Execute(coord_s, ®ex_match)) +    matched = true; +  else if (regex.Compile(llvm::StringRef("^([0-9]+),([0-9]+)$")) && +           regex.Execute(coord_s, ®ex_match)) +    matched = true; +  else if (regex.Compile(llvm::StringRef("^([0-9]+)$")) && +           regex.Execute(coord_s, ®ex_match)) +    matched = true; + +  if (!matched) +    return false; + +  auto get_index = [&](int idx, uint32_t &i) -> bool { +    std::string group; +    errno = 0; +    if (regex_match.GetMatchAtIndex(coord_s.str().c_str(), idx + 1, group)) +      return !llvm::StringRef(group).getAsInteger<uint32_t>(10, i); +    return true; +  }; + +  return get_index(0, coord.x) && get_index(1, coord.y) && +         get_index(2, coord.z); +} + +bool SkipPrologue(lldb::ModuleSP &module, Address &addr) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); +  SymbolContext sc; +  uint32_t resolved_flags = +      module->ResolveSymbolContextForAddress(addr, eSymbolContextFunction, sc); +  if (resolved_flags & eSymbolContextFunction) { +    if (sc.function) { +      const uint32_t offset = sc.function->GetPrologueByteSize(); +      ConstString name = sc.GetFunctionName(); +      if (offset) +        addr.Slide(offset); +      if (log) +        log->Printf("%s: Prologue offset for %s is %" PRIu32, __FUNCTION__, +                    name.AsCString(), offset); +    } +    return true; +  } else +    return false; +} +} // 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> res_name; +  // Path to script .so file on the device. +  empirical_type<std::string> shared_lib; +  // Directory where kernel objects are cached on device. +  empirical_type<std::string> cache_dir; +  // 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 structs +  ConstString type_name; // Name of type, only needed for structs + +  static 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 cube_map; + +    Dimension() { +      dim_1 = 0; +      dim_2 = 0; +      dim_3 = 0; +      cube_map = 0; +    } +  }; + +  // The FileHeader struct specifies the header we use for writing allocations +  // to a binary file. Our format begins with the ASCII characters "RSAD", +  // identifying the file as an allocation dump. Member variables dims and +  // hdr_size are then written consecutively, immediately followed by an +  // instance of the ElementHeader struct. Because Elements can contain +  // subelements, there may be more than one instance of the ElementHeader +  // struct. With this first instance being the root element, and the other +  // instances being the root's descendants. To identify which instances are an +  // ElementHeader's children, each struct is immediately followed by a +  // sequence of consecutive offsets to the start of its child structs. These +  // offsets are +  // 4 bytes in size, and the 0 offset signifies no more children. +  struct FileHeader { +    uint8_t ident[4];  // ASCII 'RSAD' identifying the file +    uint32_t dims[3];  // Dimensions +    uint16_t hdr_size; // Header size in bytes, including all element headers +  }; + +  struct ElementHeader { +    uint16_t type;         // DataType enum +    uint32_t kind;         // DataKind enum +    uint32_t element_size; // Size of a single element, including padding +    uint16_t vector_size;  // Vector width +    uint32_t array_size;   // Number of elements in array +  }; + +  // Monotonically increasing from 1 +  static uint32_t 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 uint32_t RSTypeToFormat[][3]; + +  // Give each allocation an ID as a way +  // for commands to reference it. +  const uint32_t id; + +  // Allocation Element type +  RenderScriptRuntime::Element element; +  // Dimensions of the Allocation +  empirical_type<Dimension> dimension; +  // Pointer to address of the RS Allocation +  empirical_type<lldb::addr_t> address; +  // Pointer to the data held by the Allocation +  empirical_type<lldb::addr_t> data_ptr; +  // Pointer to the RS Type of the Allocation +  empirical_type<lldb::addr_t> type_ptr; +  // Pointer to the RS Context of the Allocation +  empirical_type<lldb::addr_t> context; +  // Size of the allocation +  empirical_type<uint32_t> size; +  // Stride between rows of the allocation +  empirical_type<uint32_t> stride; + +  // 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(); +  } +}; + +ConstString RenderScriptRuntime::Element::GetFallbackStructName() { +  static const ConstString FallbackStructName("struct"); +  return FallbackStructName; +} + +uint32_t RenderScriptRuntime::AllocationDetails::ID = 1; + +const char *RenderScriptRuntime::AllocationDetails::RsDataKindToString[] = { +    "User",       "Undefined",   "Undefined", "Undefined", +    "Undefined",  "Undefined",   "Undefined", // Enum jumps from 0 to 7 +    "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 uint32_t RenderScriptRuntime::AllocationDetails::RSTypeToFormat[][3] = { +    // RS_TYPE_NONE +    {eFormatHex, eFormatHex, 1}, +    // RS_TYPE_FLOAT_16 +    {eFormatFloat, eFormatVectorOfFloat16, 2}, +    // RS_TYPE_FLOAT_32 +    {eFormatFloat, eFormatVectorOfFloat32, sizeof(float)}, +    // RS_TYPE_FLOAT_64 +    {eFormatFloat, eFormatVectorOfFloat64, sizeof(double)}, +    // RS_TYPE_SIGNED_8 +    {eFormatDecimal, eFormatVectorOfSInt8, sizeof(int8_t)}, +    // RS_TYPE_SIGNED_16 +    {eFormatDecimal, eFormatVectorOfSInt16, sizeof(int16_t)}, +    // RS_TYPE_SIGNED_32 +    {eFormatDecimal, eFormatVectorOfSInt32, sizeof(int32_t)}, +    // RS_TYPE_SIGNED_64 +    {eFormatDecimal, eFormatVectorOfSInt64, sizeof(int64_t)}, +    // RS_TYPE_UNSIGNED_8 +    {eFormatDecimal, eFormatVectorOfUInt8, sizeof(uint8_t)}, +    // RS_TYPE_UNSIGNED_16 +    {eFormatDecimal, eFormatVectorOfUInt16, sizeof(uint16_t)}, +    // RS_TYPE_UNSIGNED_32 +    {eFormatDecimal, eFormatVectorOfUInt32, sizeof(uint32_t)}, +    // RS_TYPE_UNSIGNED_64 +    {eFormatDecimal, eFormatVectorOfUInt64, sizeof(uint64_t)}, +    // RS_TYPE_BOOL +    {eFormatBoolean, eFormatBoolean, 1}, +    // 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 +    {eFormatHex, eFormatHex, sizeof(uint16_t)}, +    // RS_TYPE_MATRIX_4X4 +    {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 16}, +    // RS_TYPE_MATRIX_3X3 +    {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 9}, +    // RS_TYPE_MATRIX_2X2 +    {eFormatVectorOfFloat32, eFormatVectorOfFloat32, sizeof(float) * 4}}; + +// Static Functions +LanguageRuntime * +RenderScriptRuntime::CreateInstance(Process *process, +                                    lldb::LanguageType language) { + +  if (language == eLanguageTypeExtRenderScript) +    return new RenderScriptRuntime(process); +  else +    return nullptr; +} + +// 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 || !IsRenderScriptScriptModule(module)) +    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; +} + +Searcher::CallbackReturn +RSReduceBreakpointResolver::SearchCallback(lldb_private::SearchFilter &filter, +                                           lldb_private::SymbolContext &context, +                                           Address *, bool) { +  // We need to have access to the list of reductions currently parsed, as +  // reduce names don't actually exist as symbols in a module. They are only +  // identifiable by parsing the .rs.info packet, or finding the expand symbol. +  // We therefore need access to the list of parsed rs modules to properly +  // resolve reduction names. +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); +  ModuleSP module = context.module_sp; + +  if (!module || !IsRenderScriptScriptModule(module)) +    return Searcher::eCallbackReturnContinue; + +  if (!m_rsmodules) +    return Searcher::eCallbackReturnContinue; + +  for (const auto &module_desc : *m_rsmodules) { +    if (module_desc->m_module != module) +      continue; + +    for (const auto &reduction : module_desc->m_reductions) { +      if (reduction.m_reduce_name != m_reduce_name) +        continue; + +      std::array<std::pair<ConstString, int>, 5> funcs{ +          {{reduction.m_init_name, eKernelTypeInit}, +           {reduction.m_accum_name, eKernelTypeAccum}, +           {reduction.m_comb_name, eKernelTypeComb}, +           {reduction.m_outc_name, eKernelTypeOutC}, +           {reduction.m_halter_name, eKernelTypeHalter}}}; + +      for (const auto &kernel : funcs) { +        // Skip constituent functions that don't match our spec +        if (!(m_kernel_types & kernel.second)) +          continue; + +        const auto kernel_name = kernel.first; +        const auto symbol = module->FindFirstSymbolWithNameAndType( +            kernel_name, eSymbolTypeCode); +        if (!symbol) +          continue; + +        auto address = symbol->GetAddress(); +        if (filter.AddressPasses(address)) { +          bool new_bp; +          if (!SkipPrologue(module, address)) { +            if (log) +              log->Printf("%s: Error trying to skip prologue", __FUNCTION__); +          } +          m_breakpoint->AddLocation(address, &new_bp); +          if (log) +            log->Printf("%s: %s reduction breakpoint on %s in %s", __FUNCTION__, +                        new_bp ? "new" : "existing", kernel_name.GetCString(), +                        address.GetModule()->GetFileSpec().GetCString()); +        } +      } +    } +  } +  return eCallbackReturnContinue; +} + +Searcher::CallbackReturn RSScriptGroupBreakpointResolver::SearchCallback( +    SearchFilter &filter, SymbolContext &context, Address *addr, +    bool containing) { + +  if (!m_breakpoint) +    return eCallbackReturnContinue; + +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); +  ModuleSP &module = context.module_sp; + +  if (!module || !IsRenderScriptScriptModule(module)) +    return Searcher::eCallbackReturnContinue; + +  std::vector<std::string> names; +  m_breakpoint->GetNames(names); +  if (names.empty()) +    return eCallbackReturnContinue; + +  for (auto &name : names) { +    const RSScriptGroupDescriptorSP sg = FindScriptGroup(ConstString(name)); +    if (!sg) { +      if (log) +        log->Printf("%s: could not find script group for %s", __FUNCTION__, +                    name.c_str()); +      continue; +    } + +    if (log) +      log->Printf("%s: Found ScriptGroup for %s", __FUNCTION__, name.c_str()); + +    for (const RSScriptGroupDescriptor::Kernel &k : sg->m_kernels) { +      if (log) { +        log->Printf("%s: Adding breakpoint for %s", __FUNCTION__, +                    k.m_name.AsCString()); +        log->Printf("%s: Kernel address 0x%" PRIx64, __FUNCTION__, k.m_addr); +      } + +      const lldb_private::Symbol *sym = +          module->FindFirstSymbolWithNameAndType(k.m_name, eSymbolTypeCode); +      if (!sym) { +        if (log) +          log->Printf("%s: Unable to find symbol for %s", __FUNCTION__, +                      k.m_name.AsCString()); +        continue; +      } + +      if (log) { +        log->Printf("%s: Found symbol name is %s", __FUNCTION__, +                    sym->GetName().AsCString()); +      } + +      auto address = sym->GetAddress(); +      if (!SkipPrologue(module, address)) { +        if (log) +          log->Printf("%s: Error trying to skip prologue", __FUNCTION__); +      } + +      bool new_bp; +      m_breakpoint->AddLocation(address, &new_bp); + +      if (log) +        log->Printf("%s: Placed %sbreakpoint on %s", __FUNCTION__, +                    new_bp ? "new " : "", k.m_name.AsCString()); + +      // exit after placing the first breakpoint if we do not intend to stop on +      // all kernels making up this script group +      if (!m_stop_on_all) +        break; +    } +  } + +  return eCallbackReturnContinue; +} + +void RenderScriptRuntime::Initialize() { +  PluginManager::RegisterPlugin(GetPluginNameStatic(), +                                "RenderScript language support", CreateInstance, +                                GetCommandObject); +} + +void RenderScriptRuntime::Terminate() { +  PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString RenderScriptRuntime::GetPluginNameStatic() { +  static ConstString plugin_name("renderscript"); +  return plugin_name; +} + +RenderScriptRuntime::ModuleKind +RenderScriptRuntime::GetModuleKind(const lldb::ModuleSP &module_sp) { +  if (module_sp) { +    if (IsRenderScriptScriptModule(module_sp)) +      return eModuleKindKernelObj; + +    // Is this the main RS runtime library +    const ConstString rs_lib("libRS.so"); +    if (module_sp->GetFileSpec().GetFilename() == rs_lib) { +      return eModuleKindLibRS; +    } + +    const ConstString rs_driverlib("libRSDriver.so"); +    if (module_sp->GetFileSpec().GetFilename() == rs_driverlib) { +      return eModuleKindDriver; +    } + +    const ConstString rs_cpureflib("libRSCpuRef.so"); +    if (module_sp->GetFileSpec().GetFilename() == rs_cpureflib) { +      return eModuleKindImpl; +    } +  } +  return eModuleKindIgnored; +} + +bool RenderScriptRuntime::IsRenderScriptModule( +    const lldb::ModuleSP &module_sp) { +  return GetModuleKind(module_sp) != eModuleKindIgnored; +} + +void RenderScriptRuntime::ModulesDidLoad(const ModuleList &module_list) { +  std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex()); + +  size_t num_modules = module_list.GetSize(); +  for (size_t i = 0; i < num_modules; i++) { +    auto mod = module_list.GetModuleAtIndex(i); +    if (IsRenderScriptModule(mod)) { +      LoadModule(mod); +    } +  } +} + +// PluginInterface protocol +lldb_private::ConstString RenderScriptRuntime::GetPluginName() { +  return GetPluginNameStatic(); +} + +uint32_t RenderScriptRuntime::GetPluginVersion() { return 1; } + +bool RenderScriptRuntime::GetDynamicTypeAndAddress( +    ValueObject &in_value, lldb::DynamicValueType use_dynamic, +    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) { +  return false; +} + +lldb::BreakpointResolverSP +RenderScriptRuntime::CreateExceptionResolver(Breakpoint *bp, bool catch_bp, +                                             bool throw_bp) { +  BreakpointResolverSP resolver_sp; +  return resolver_sp; +} + +const RenderScriptRuntime::HookDefn RenderScriptRuntime::s_runtimeHookDefns[] = +    { +        // rsdScript +        {"rsdScriptInit", "_Z13rsdScriptInitPKN7android12renderscript7ContextEP" +                          "NS0_7ScriptCEPKcS7_PKhjj", +         "_Z13rsdScriptInitPKN7android12renderscript7ContextEPNS0_" +         "7ScriptCEPKcS7_PKhmj", +         0, RenderScriptRuntime::eModuleKindDriver, +         &lldb_private::RenderScriptRuntime::CaptureScriptInit}, +        {"rsdScriptInvokeForEachMulti", +         "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0" +         "_6ScriptEjPPKNS0_10AllocationEjPS6_PKvjPK12RsScriptCall", +         "_Z27rsdScriptInvokeForEachMultiPKN7android12renderscript7ContextEPNS0" +         "_6ScriptEjPPKNS0_10AllocationEmPS6_PKvmPK12RsScriptCall", +         0, RenderScriptRuntime::eModuleKindDriver, +         &lldb_private::RenderScriptRuntime::CaptureScriptInvokeForEachMulti}, +        {"rsdScriptSetGlobalVar", "_Z21rsdScriptSetGlobalVarPKN7android12render" +                                  "script7ContextEPKNS0_6ScriptEjPvj", +         "_Z21rsdScriptSetGlobalVarPKN7android12renderscript7ContextEPKNS0_" +         "6ScriptEjPvm", +         0, RenderScriptRuntime::eModuleKindDriver, +         &lldb_private::RenderScriptRuntime::CaptureSetGlobalVar}, + +        // rsdAllocation +        {"rsdAllocationInit", "_Z17rsdAllocationInitPKN7android12renderscript7C" +                              "ontextEPNS0_10AllocationEb", +         "_Z17rsdAllocationInitPKN7android12renderscript7ContextEPNS0_" +         "10AllocationEb", +         0, RenderScriptRuntime::eModuleKindDriver, +         &lldb_private::RenderScriptRuntime::CaptureAllocationInit}, +        {"rsdAllocationRead2D", +         "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_" +         "10AllocationEjjj23RsAllocationCubemapFacejjPvjj", +         "_Z19rsdAllocationRead2DPKN7android12renderscript7ContextEPKNS0_" +         "10AllocationEjjj23RsAllocationCubemapFacejjPvmm", +         0, RenderScriptRuntime::eModuleKindDriver, nullptr}, +        {"rsdAllocationDestroy", "_Z20rsdAllocationDestroyPKN7android12rendersc" +                                 "ript7ContextEPNS0_10AllocationE", +         "_Z20rsdAllocationDestroyPKN7android12renderscript7ContextEPNS0_" +         "10AllocationE", +         0, RenderScriptRuntime::eModuleKindDriver, +         &lldb_private::RenderScriptRuntime::CaptureAllocationDestroy}, + +        // renderscript script groups +        {"rsdDebugHintScriptGroup2", "_ZN7android12renderscript21debugHintScrip" +                                     "tGroup2EPKcjPKPFvPK24RsExpandKernelDriver" +                                     "InfojjjEj", +         "_ZN7android12renderscript21debugHintScriptGroup2EPKcjPKPFvPK24RsExpan" +         "dKernelDriverInfojjjEj", +         0, RenderScriptRuntime::eModuleKindImpl, +         &lldb_private::RenderScriptRuntime::CaptureDebugHintScriptGroup2}}; + +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) { +  RuntimeHook *hook = (RuntimeHook *)baton; +  ExecutionContext exe_ctx(ctx->exe_ctx_ref); + +  RenderScriptRuntime *lang_rt = llvm::cast<RenderScriptRuntime>( +      exe_ctx.GetProcessPtr()->GetLanguageRuntime( +          eLanguageTypeExtRenderScript)); + +  lang_rt->HookCallback(hook, exe_ctx); + +  return false; +} + +void RenderScriptRuntime::HookCallback(RuntimeHook *hook, +                                       ExecutionContext &exe_ctx) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (log) +    log->Printf("%s - '%s'", __FUNCTION__, hook->defn->name); + +  if (hook->defn->grabber) { +    (this->*(hook->defn->grabber))(hook, exe_ctx); +  } +} + +void RenderScriptRuntime::CaptureDebugHintScriptGroup2( +    RuntimeHook *hook_info, ExecutionContext &context) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  enum { +    eGroupName = 0, +    eGroupNameSize, +    eKernel, +    eKernelCount, +  }; + +  std::array<ArgItem, 4> args{{ +      {ArgItem::ePointer, 0}, // const char         *groupName +      {ArgItem::eInt32, 0},   // const uint32_t      groupNameSize +      {ArgItem::ePointer, 0}, // const ExpandFuncTy *kernel +      {ArgItem::eInt32, 0},   // const uint32_t      kernelCount +  }}; + +  if (!GetArgs(context, args.data(), args.size())) { +    if (log) +      log->Printf("%s - Error while reading the function parameters", +                  __FUNCTION__); +    return; +  } else if (log) { +    log->Printf("%s - groupName    : 0x%" PRIx64, __FUNCTION__, +                addr_t(args[eGroupName])); +    log->Printf("%s - groupNameSize: %" PRIu64, __FUNCTION__, +                uint64_t(args[eGroupNameSize])); +    log->Printf("%s - kernel       : 0x%" PRIx64, __FUNCTION__, +                addr_t(args[eKernel])); +    log->Printf("%s - kernelCount  : %" PRIu64, __FUNCTION__, +                uint64_t(args[eKernelCount])); +  } + +  // parse script group name +  ConstString group_name; +  { +    Status err; +    const uint64_t len = uint64_t(args[eGroupNameSize]); +    std::unique_ptr<char[]> buffer(new char[uint32_t(len + 1)]); +    m_process->ReadMemory(addr_t(args[eGroupName]), buffer.get(), len, err); +    buffer.get()[len] = '\0'; +    if (!err.Success()) { +      if (log) +        log->Printf("Error reading scriptgroup name from target"); +      return; +    } else { +      if (log) +        log->Printf("Extracted scriptgroup name %s", buffer.get()); +    } +    // write back the script group name +    group_name.SetCString(buffer.get()); +  } + +  // create or access existing script group +  RSScriptGroupDescriptorSP group; +  { +    // search for existing script group +    for (auto sg : m_scriptGroups) { +      if (sg->m_name == group_name) { +        group = sg; +        break; +      } +    } +    if (!group) { +      group = std::make_shared<RSScriptGroupDescriptor>(); +      group->m_name = group_name; +      m_scriptGroups.push_back(group); +    } else { +      // already have this script group +      if (log) +        log->Printf("Attempt to add duplicate script group %s", +                    group_name.AsCString()); +      return; +    } +  } +  assert(group); + +  const uint32_t target_ptr_size = m_process->GetAddressByteSize(); +  std::vector<addr_t> kernels; +  // parse kernel addresses in script group +  for (uint64_t i = 0; i < uint64_t(args[eKernelCount]); ++i) { +    RSScriptGroupDescriptor::Kernel kernel; +    // extract script group kernel addresses from the target +    const addr_t ptr_addr = addr_t(args[eKernel]) + i * target_ptr_size; +    uint64_t kernel_addr = 0; +    Status err; +    size_t read = +        m_process->ReadMemory(ptr_addr, &kernel_addr, target_ptr_size, err); +    if (!err.Success() || read != target_ptr_size) { +      if (log) +        log->Printf("Error parsing kernel address %" PRIu64 " in script group", +                    i); +      return; +    } +    if (log) +      log->Printf("Extracted scriptgroup kernel address - 0x%" PRIx64, +                  kernel_addr); +    kernel.m_addr = kernel_addr; + +    // try to resolve the associated kernel name +    if (!ResolveKernelName(kernel.m_addr, kernel.m_name)) { +      if (log) +        log->Printf("Parsed scriptgroup kernel %" PRIu64 " - 0x%" PRIx64, i, +                    kernel_addr); +      return; +    } + +    // try to find the non '.expand' function +    { +      const llvm::StringRef expand(".expand"); +      const llvm::StringRef name_ref = kernel.m_name.GetStringRef(); +      if (name_ref.endswith(expand)) { +        const ConstString base_kernel(name_ref.drop_back(expand.size())); +        // verify this function is a valid kernel +        if (IsKnownKernel(base_kernel)) { +          kernel.m_name = base_kernel; +          if (log) +            log->Printf("%s - found non expand version '%s'", __FUNCTION__, +                        base_kernel.GetCString()); +        } +      } +    } +    // add to a list of script group kernels we know about +    group->m_kernels.push_back(kernel); +  } + +  // Resolve any pending scriptgroup breakpoints +  { +    Target &target = m_process->GetTarget(); +    const BreakpointList &list = target.GetBreakpointList(); +    const size_t num_breakpoints = list.GetSize(); +    if (log) +      log->Printf("Resolving %zu breakpoints", num_breakpoints); +    for (size_t i = 0; i < num_breakpoints; ++i) { +      const BreakpointSP bp = list.GetBreakpointAtIndex(i); +      if (bp) { +        if (bp->MatchesName(group_name.AsCString())) { +          if (log) +            log->Printf("Found breakpoint with name %s", +                        group_name.AsCString()); +          bp->ResolveBreakpoint(); +        } +      } +    } +  } +} + +void RenderScriptRuntime::CaptureScriptInvokeForEachMulti( +    RuntimeHook *hook, ExecutionContext &exe_ctx) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  enum { +    eRsContext = 0, +    eRsScript, +    eRsSlot, +    eRsAIns, +    eRsInLen, +    eRsAOut, +    eRsUsr, +    eRsUsrLen, +    eRsSc, +  }; + +  std::array<ArgItem, 9> args{{ +      ArgItem{ArgItem::ePointer, 0}, // const Context       *rsc +      ArgItem{ArgItem::ePointer, 0}, // Script              *s +      ArgItem{ArgItem::eInt32, 0},   // uint32_t             slot +      ArgItem{ArgItem::ePointer, 0}, // const Allocation   **aIns +      ArgItem{ArgItem::eInt32, 0},   // size_t               inLen +      ArgItem{ArgItem::ePointer, 0}, // Allocation          *aout +      ArgItem{ArgItem::ePointer, 0}, // const void          *usr +      ArgItem{ArgItem::eInt32, 0},   // size_t               usrLen +      ArgItem{ArgItem::ePointer, 0}, // const RsScriptCall  *sc +  }}; + +  bool success = GetArgs(exe_ctx, &args[0], args.size()); +  if (!success) { +    if (log) +      log->Printf("%s - Error while reading the function parameters", +                  __FUNCTION__); +    return; +  } + +  const uint32_t target_ptr_size = m_process->GetAddressByteSize(); +  Status err; +  std::vector<uint64_t> allocs; + +  // traverse allocation list +  for (uint64_t i = 0; i < uint64_t(args[eRsInLen]); ++i) { +    // calculate offest to allocation pointer +    const addr_t addr = addr_t(args[eRsAIns]) + i * target_ptr_size; + +    // Note: due to little endian layout, reading 32bits or 64bits into res +    // will give the correct results. +    uint64_t result = 0; +    size_t read = m_process->ReadMemory(addr, &result, target_ptr_size, err); +    if (read != target_ptr_size || !err.Success()) { +      if (log) +        log->Printf( +            "%s - Error while reading allocation list argument %" PRIu64, +            __FUNCTION__, i); +    } else { +      allocs.push_back(result); +    } +  } + +  // if there is an output allocation track it +  if (uint64_t alloc_out = uint64_t(args[eRsAOut])) { +    allocs.push_back(alloc_out); +  } + +  // for all allocations we have found +  for (const uint64_t alloc_addr : allocs) { +    AllocationDetails *alloc = LookUpAllocation(alloc_addr); +    if (!alloc) +      alloc = CreateAllocation(alloc_addr); + +    if (alloc) { +      // save the allocation address +      if (alloc->address.isValid()) { +        // check the allocation address we already have matches +        assert(*alloc->address.get() == alloc_addr); +      } else { +        alloc->address = alloc_addr; +      } + +      // save the context +      if (log) { +        if (alloc->context.isValid() && +            *alloc->context.get() != addr_t(args[eRsContext])) +          log->Printf("%s - Allocation used by multiple contexts", +                      __FUNCTION__); +      } +      alloc->context = addr_t(args[eRsContext]); +    } +  } + +  // make sure we track this script object +  if (lldb_private::RenderScriptRuntime::ScriptDetails *script = +          LookUpScript(addr_t(args[eRsScript]), true)) { +    if (log) { +      if (script->context.isValid() && +          *script->context.get() != addr_t(args[eRsContext])) +        log->Printf("%s - Script used by multiple contexts", __FUNCTION__); +    } +    script->context = addr_t(args[eRsContext]); +  } +} + +void RenderScriptRuntime::CaptureSetGlobalVar(RuntimeHook *hook, +                                              ExecutionContext &context) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  enum { +    eRsContext, +    eRsScript, +    eRsId, +    eRsData, +    eRsLength, +  }; + +  std::array<ArgItem, 5> args{{ +      ArgItem{ArgItem::ePointer, 0}, // eRsContext +      ArgItem{ArgItem::ePointer, 0}, // eRsScript +      ArgItem{ArgItem::eInt32, 0},   // eRsId +      ArgItem{ArgItem::ePointer, 0}, // eRsData +      ArgItem{ArgItem::eInt32, 0},   // eRsLength +  }}; + +  bool success = GetArgs(context, &args[0], args.size()); +  if (!success) { +    if (log) +      log->Printf("%s - error reading the function parameters.", __FUNCTION__); +    return; +  } + +  if (log) { +    log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 " slot %" PRIu64 " = 0x%" PRIx64 +                ":%" PRIu64 "bytes.", +                __FUNCTION__, uint64_t(args[eRsContext]), +                uint64_t(args[eRsScript]), uint64_t(args[eRsId]), +                uint64_t(args[eRsData]), uint64_t(args[eRsLength])); + +    addr_t script_addr = addr_t(args[eRsScript]); +    if (m_scriptMappings.find(script_addr) != m_scriptMappings.end()) { +      auto rsm = m_scriptMappings[script_addr]; +      if (uint64_t(args[eRsId]) < rsm->m_globals.size()) { +        auto rsg = rsm->m_globals[uint64_t(args[eRsId])]; +        log->Printf("%s - Setting of '%s' within '%s' inferred", __FUNCTION__, +                    rsg.m_name.AsCString(), +                    rsm->m_module->GetFileSpec().GetFilename().AsCString()); +      } +    } +  } +} + +void RenderScriptRuntime::CaptureAllocationInit(RuntimeHook *hook, +                                                ExecutionContext &exe_ctx) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  enum { eRsContext, eRsAlloc, eRsForceZero }; + +  std::array<ArgItem, 3> args{{ +      ArgItem{ArgItem::ePointer, 0}, // eRsContext +      ArgItem{ArgItem::ePointer, 0}, // eRsAlloc +      ArgItem{ArgItem::eBool, 0},    // eRsForceZero +  }}; + +  bool success = GetArgs(exe_ctx, &args[0], args.size()); +  if (!success) { +    if (log) +      log->Printf("%s - error while reading the function parameters", +                  __FUNCTION__); +    return; +  } + +  if (log) +    log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 ",0x%" PRIx64 " .", +                __FUNCTION__, uint64_t(args[eRsContext]), +                uint64_t(args[eRsAlloc]), uint64_t(args[eRsForceZero])); + +  AllocationDetails *alloc = CreateAllocation(uint64_t(args[eRsAlloc])); +  if (alloc) +    alloc->context = uint64_t(args[eRsContext]); +} + +void RenderScriptRuntime::CaptureAllocationDestroy(RuntimeHook *hook, +                                                   ExecutionContext &exe_ctx) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  enum { +    eRsContext, +    eRsAlloc, +  }; + +  std::array<ArgItem, 2> args{{ +      ArgItem{ArgItem::ePointer, 0}, // eRsContext +      ArgItem{ArgItem::ePointer, 0}, // eRsAlloc +  }}; + +  bool success = GetArgs(exe_ctx, &args[0], args.size()); +  if (!success) { +    if (log) +      log->Printf("%s - error while reading the function parameters.", +                  __FUNCTION__); +    return; +  } + +  if (log) +    log->Printf("%s - 0x%" PRIx64 ", 0x%" PRIx64 ".", __FUNCTION__, +                uint64_t(args[eRsContext]), uint64_t(args[eRsAlloc])); + +  for (auto iter = m_allocations.begin(); iter != m_allocations.end(); ++iter) { +    auto &allocation_up = *iter; // get the unique pointer +    if (allocation_up->address.isValid() && +        *allocation_up->address.get() == addr_t(args[eRsAlloc])) { +      m_allocations.erase(iter); +      if (log) +        log->Printf("%s - deleted allocation entry.", __FUNCTION__); +      return; +    } +  } + +  if (log) +    log->Printf("%s - couldn't find destroyed allocation.", __FUNCTION__); +} + +void RenderScriptRuntime::CaptureScriptInit(RuntimeHook *hook, +                                            ExecutionContext &exe_ctx) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  Status err; +  Process *process = exe_ctx.GetProcessPtr(); + +  enum { eRsContext, eRsScript, eRsResNamePtr, eRsCachedDirPtr }; + +  std::array<ArgItem, 4> args{ +      {ArgItem{ArgItem::ePointer, 0}, ArgItem{ArgItem::ePointer, 0}, +       ArgItem{ArgItem::ePointer, 0}, ArgItem{ArgItem::ePointer, 0}}}; +  bool success = GetArgs(exe_ctx, &args[0], args.size()); +  if (!success) { +    if (log) +      log->Printf("%s - error while reading the function parameters.", +                  __FUNCTION__); +    return; +  } + +  std::string res_name; +  process->ReadCStringFromMemory(addr_t(args[eRsResNamePtr]), res_name, err); +  if (err.Fail()) { +    if (log) +      log->Printf("%s - error reading res_name: %s.", __FUNCTION__, +                  err.AsCString()); +  } + +  std::string cache_dir; +  process->ReadCStringFromMemory(addr_t(args[eRsCachedDirPtr]), cache_dir, err); +  if (err.Fail()) { +    if (log) +      log->Printf("%s - error reading cache_dir: %s.", __FUNCTION__, +                  err.AsCString()); +  } + +  if (log) +    log->Printf("%s - 0x%" PRIx64 ",0x%" PRIx64 " => '%s' at '%s' .", +                __FUNCTION__, uint64_t(args[eRsContext]), +                uint64_t(args[eRsScript]), res_name.c_str(), cache_dir.c_str()); + +  if (res_name.size() > 0) { +    StreamString strm; +    strm.Printf("librs.%s.so", res_name.c_str()); + +    ScriptDetails *script = LookUpScript(addr_t(args[eRsScript]), true); +    if (script) { +      script->type = ScriptDetails::eScriptC; +      script->cache_dir = cache_dir; +      script->res_name = res_name; +      script->shared_lib = strm.GetString(); +      script->context = addr_t(args[eRsContext]); +    } + +    if (log) +      log->Printf("%s - '%s' tagged with context 0x%" PRIx64 +                  " and script 0x%" PRIx64 ".", +                  __FUNCTION__, strm.GetData(), uint64_t(args[eRsContext]), +                  uint64_t(args[eRsScript])); +  } else if (log) { +    log->Printf("%s - resource name invalid, Script not tagged.", __FUNCTION__); +  } +} + +void RenderScriptRuntime::LoadRuntimeHooks(lldb::ModuleSP module, +                                           ModuleKind kind) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (!module) { +    return; +  } + +  Target &target = GetProcess()->GetTarget(); +  const llvm::Triple::ArchType machine = target.GetArchitecture().GetMachine(); + +  if (machine != llvm::Triple::ArchType::x86 && +      machine != llvm::Triple::ArchType::arm && +      machine != llvm::Triple::ArchType::aarch64 && +      machine != llvm::Triple::ArchType::mipsel && +      machine != llvm::Triple::ArchType::mips64el && +      machine != llvm::Triple::ArchType::x86_64) { +    if (log) +      log->Printf("%s - unable to hook runtime functions.", __FUNCTION__); +    return; +  } + +  const uint32_t target_ptr_size = +      target.GetArchitecture().GetAddressByteSize(); + +  std::array<bool, s_runtimeHookCount> hook_placed; +  hook_placed.fill(false); + +  for (size_t idx = 0; idx < s_runtimeHookCount; idx++) { +    const HookDefn *hook_defn = &s_runtimeHookDefns[idx]; +    if (hook_defn->kind != kind) { +      continue; +    } + +    const char *symbol_name = (target_ptr_size == 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("%s - symbol '%s' related to the function %s not found", +                    __FUNCTION__, symbol_name, hook_defn->name); +      } +      continue; +    } + +    addr_t addr = sym->GetLoadAddress(&target); +    if (addr == LLDB_INVALID_ADDRESS) { +      if (log) +        log->Printf("%s - unable to resolve the address of hook function '%s' " +                    "with symbol '%s'.", +                    __FUNCTION__, hook_defn->name, symbol_name); +      continue; +    } else { +      if (log) +        log->Printf("%s - function %s, address resolved at 0x%" PRIx64, +                    __FUNCTION__, hook_defn->name, addr); +    } + +    RuntimeHookSP hook(new RuntimeHook()); +    hook->address = addr; +    hook->defn = hook_defn; +    hook->bp_sp = target.CreateBreakpoint(addr, true, false); +    hook->bp_sp->SetCallback(HookCallback, hook.get(), true); +    m_runtimeHooks[addr] = hook; +    if (log) { +      log->Printf("%s - successfully hooked '%s' in '%s' version %" PRIu64 +                  " at 0x%" PRIx64 ".", +                  __FUNCTION__, hook_defn->name, +                  module->GetFileSpec().GetFilename().AsCString(), +                  (uint64_t)hook_defn->version, (uint64_t)addr); +    } +    hook_placed[idx] = true; +  } + +  // log any unhooked function +  if (log) { +    for (size_t i = 0; i < hook_placed.size(); ++i) { +      if (hook_placed[i]) +        continue; +      const HookDefn &hook_defn = s_runtimeHookDefns[i]; +      if (hook_defn.kind != kind) +        continue; +      log->Printf("%s - function %s was not hooked", __FUNCTION__, +                  hook_defn.name); +    } +  } +} + +void RenderScriptRuntime::FixupScriptDetails(RSModuleDescriptorSP rsmodule_sp) { +  if (!rsmodule_sp) +    return; + +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  const ModuleSP module = rsmodule_sp->m_module; +  const FileSpec &file = module->GetPlatformFileSpec(); + +  // 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 shared_lib; +    if (!rs_script->shared_lib.get(shared_lib)) +      continue; + +    // Only proceed if the module that has loaded corresponds to this script. +    if (file.GetFilename() != ConstString(shared_lib.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( +              "%s - script %" PRIx64 " wants reassigned to new rsmodule '%s'.", +              __FUNCTION__, (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 res_name; +      if (rs_script->res_name.get(res_name)) +        // Set the modules resource name. +        rsmodule_sp->m_resname = res_name; +      // Add Script/Module pair to map. +      m_scriptMappings[script] = rsmodule_sp; +      if (log) +        log->Printf( +            "%s - script %" PRIx64 " associated with rsmodule '%s'.", +            __FUNCTION__, (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* parameter. Function returns true on success, and false on +// failure +bool RenderScriptRuntime::EvalRSExpression(const char *expr, +                                           StackFrame *frame_ptr, +                                           uint64_t *result) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); +  if (log) +    log->Printf("%s(%s)", __FUNCTION__, expr); + +  ValueObjectSP expr_result; +  EvaluateExpressionOptions options; +  options.SetLanguage(lldb::eLanguageTypeC_plus_plus); +  // Perform the actual expression evaluation +  auto &target = GetProcess()->GetTarget(); +  target.EvaluateExpression(expr, frame_ptr, expr_result, options); + +  if (!expr_result) { +    if (log) +      log->Printf("%s: couldn't evaluate expression.", __FUNCTION__); +    return false; +  } + +  // The result of the expression is invalid +  if (!expr_result->GetError().Success()) { +    Status err = expr_result->GetError(); +    // Expression returned is void, so this is actually a success +    if (err.GetError() == UserExpression::kNoResult) { +      if (log) +        log->Printf("%s - expression returned void.", __FUNCTION__); + +      result = nullptr; +      return true; +    } + +    if (log) +      log->Printf("%s - error evaluating expression result: %s", __FUNCTION__, +                  err.AsCString()); +    return false; +  } + +  bool success = false; +  // We only read the result as an uint32_t. +  *result = expr_result->GetValueAsUnsigned(0, &success); + +  if (!success) { +    if (log) +      log->Printf("%s - couldn't convert expression result to uint32_t", +                  __FUNCTION__); +    return false; +  } + +  return true; +} + +namespace { +// Used to index expression format strings +enum ExpressionStrings { +  eExprGetOffsetPtr = 0, +  eExprAllocGetType, +  eExprTypeDimX, +  eExprTypeDimY, +  eExprTypeDimZ, +  eExprTypeElemPtr, +  eExprElementType, +  eExprElementKind, +  eExprElementVec, +  eExprElementFieldCount, +  eExprSubelementsId, +  eExprSubelementsName, +  eExprSubelementsArrSize, + +  _eExprLast // keep at the end, implicit size of the array runtime_expressions +}; + +// max length of an expanded expression +const int jit_max_expr_size = 512; + +// Retrieve the string to JIT for the given expression +#define JIT_TEMPLATE_CONTEXT "void* ctxt = (void*)rsDebugGetContextWrapper(0x%" PRIx64 "); " +const char *JITTemplate(ExpressionStrings e) { +  // Format strings containing the expressions we may need to evaluate. +  static std::array<const char *, _eExprLast> runtime_expressions = { +      {// Mangled GetOffsetPointer(Allocation*, xoff, yoff, zoff, lod, cubemap) +       "(int*)_" +       "Z12GetOffsetPtrPKN7android12renderscript10AllocationEjjjj23RsAllocation" +       "CubemapFace" +       "(0x%" PRIx64 ", %" PRIu32 ", %" PRIu32 ", %" PRIu32 ", 0, 0)", // eExprGetOffsetPtr + +       // Type* rsaAllocationGetType(Context*, Allocation*) +       JIT_TEMPLATE_CONTEXT "(void*)rsaAllocationGetType(ctxt, 0x%" PRIx64 ")", // eExprAllocGetType + +       // 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 +       JIT_TEMPLATE_CONTEXT +       "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt" +       ", 0x%" PRIx64 ", data, 6); data[0]", // eExprTypeDimX +       JIT_TEMPLATE_CONTEXT +       "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt" +       ", 0x%" PRIx64 ", data, 6); data[1]", // eExprTypeDimY +       JIT_TEMPLATE_CONTEXT +       "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt" +       ", 0x%" PRIx64 ", data, 6); data[2]", // eExprTypeDimZ +       JIT_TEMPLATE_CONTEXT +       "uint%" PRIu32 "_t data[6]; (void*)rsaTypeGetNativeData(ctxt" +       ", 0x%" PRIx64 ", data, 6); data[5]", // eExprTypeElemPtr + +       // rsaElementGetNativeData(Context*, Element*, uint32_t* elemData,size) +       // Pack mType; mKind; mNormalized; mVectorSize; NumSubElements into +       // elemData +       JIT_TEMPLATE_CONTEXT +       "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt" +       ", 0x%" PRIx64 ", data, 5); data[0]", // eExprElementType +       JIT_TEMPLATE_CONTEXT +       "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt" +       ", 0x%" PRIx64 ", data, 5); data[1]", // eExprElementKind +       JIT_TEMPLATE_CONTEXT +       "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt" +       ", 0x%" PRIx64 ", data, 5); data[3]", // eExprElementVec +       JIT_TEMPLATE_CONTEXT +       "uint32_t data[5]; (void*)rsaElementGetNativeData(ctxt" +       ", 0x%" PRIx64 ", data, 5); data[4]", // eExprElementFieldCount + +       // 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 Element* of field +       JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32 +       "]; size_t arr_size[%" PRIu32 "];" +       "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64 +       ", ids, names, arr_size, %" PRIu32 "); ids[%" PRIu32 "]", // eExprSubelementsId + +       // Name of field +       JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32 +       "]; size_t arr_size[%" PRIu32 "];" +       "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64 +       ", ids, names, arr_size, %" PRIu32 "); names[%" PRIu32 "]", // eExprSubelementsName + +       // Array size of field +       JIT_TEMPLATE_CONTEXT "void* ids[%" PRIu32 "]; const char* names[%" PRIu32 +       "]; size_t arr_size[%" PRIu32 "];" +       "(void*)rsaElementGetSubElements(ctxt, 0x%" PRIx64 +       ", ids, names, arr_size, %" PRIu32 "); arr_size[%" PRIu32 "]"}}; // eExprSubelementsArrSize + +  return runtime_expressions[e]; +} +} // end of the anonymous namespace + +// 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 *alloc, +                                         StackFrame *frame_ptr, uint32_t x, +                                         uint32_t y, uint32_t z) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (!alloc->address.isValid()) { +    if (log) +      log->Printf("%s - failed to find allocation details.", __FUNCTION__); +    return false; +  } + +  const char *fmt_str = JITTemplate(eExprGetOffsetPtr); +  char expr_buf[jit_max_expr_size]; + +  int written = snprintf(expr_buf, jit_max_expr_size, fmt_str, +                         *alloc->address.get(), x, y, z); +  if (written < 0) { +    if (log) +      log->Printf("%s - encoding error in snprintf().", __FUNCTION__); +    return false; +  } else if (written >= jit_max_expr_size) { +    if (log) +      log->Printf("%s - expression too long.", __FUNCTION__); +    return false; +  } + +  uint64_t result = 0; +  if (!EvalRSExpression(expr_buf, frame_ptr, &result)) +    return false; + +  addr_t data_ptr = static_cast<lldb::addr_t>(result); +  alloc->data_ptr = data_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 *alloc, +                                         StackFrame *frame_ptr) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (!alloc->address.isValid() || !alloc->context.isValid()) { +    if (log) +      log->Printf("%s - failed to find allocation details.", __FUNCTION__); +    return false; +  } + +  const char *fmt_str = JITTemplate(eExprAllocGetType); +  char expr_buf[jit_max_expr_size]; + +  int written = snprintf(expr_buf, jit_max_expr_size, fmt_str, +                         *alloc->context.get(), *alloc->address.get()); +  if (written < 0) { +    if (log) +      log->Printf("%s - encoding error in snprintf().", __FUNCTION__); +    return false; +  } else if (written >= jit_max_expr_size) { +    if (log) +      log->Printf("%s - expression too long.", __FUNCTION__); +    return false; +  } + +  uint64_t result = 0; +  if (!EvalRSExpression(expr_buf, frame_ptr, &result)) +    return false; + +  addr_t type_ptr = static_cast<lldb::addr_t>(result); +  alloc->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 *alloc, +                                        StackFrame *frame_ptr) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (!alloc->type_ptr.isValid() || !alloc->context.isValid()) { +    if (log) +      log->Printf("%s - Failed to find allocation details.", __FUNCTION__); +    return false; +  } + +  // Expression is different depending on if device is 32 or 64 bit +  uint32_t target_ptr_size = +      GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize(); +  const uint32_t bits = target_ptr_size == 4 ? 32 : 64; + +  // We want 4 elements from packed data +  const uint32_t num_exprs = 4; +  static_assert(num_exprs == (eExprTypeElemPtr - eExprTypeDimX + 1), +                "Invalid number of expressions"); + +  char expr_bufs[num_exprs][jit_max_expr_size]; +  uint64_t results[num_exprs]; + +  for (uint32_t i = 0; i < num_exprs; ++i) { +    const char *fmt_str = JITTemplate(ExpressionStrings(eExprTypeDimX + i)); +    int written = snprintf(expr_bufs[i], jit_max_expr_size, fmt_str, +                           *alloc->context.get(), bits, *alloc->type_ptr.get()); +    if (written < 0) { +      if (log) +        log->Printf("%s - encoding error in snprintf().", __FUNCTION__); +      return false; +    } else if (written >= jit_max_expr_size) { +      if (log) +        log->Printf("%s - expression too long.", __FUNCTION__); +      return false; +    } + +    // Perform expression evaluation +    if (!EvalRSExpression(expr_bufs[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]); +  alloc->dimension = dims; + +  addr_t element_ptr = static_cast<lldb::addr_t>(results[3]); +  alloc->element.element_ptr = element_ptr; + +  if (log) +    log->Printf("%s - dims (%" PRIu32 ", %" PRIu32 ", %" PRIu32 +                ") Element*: 0x%" PRIx64 ".", +                __FUNCTION__, dims.dim_1, dims.dim_2, dims.dim_3, element_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("%s - failed to find allocation details.", __FUNCTION__); +    return false; +  } + +  // We want 4 elements from packed data +  const uint32_t num_exprs = 4; +  static_assert(num_exprs == (eExprElementFieldCount - eExprElementType + 1), +                "Invalid number of expressions"); + +  char expr_bufs[num_exprs][jit_max_expr_size]; +  uint64_t results[num_exprs]; + +  for (uint32_t i = 0; i < num_exprs; i++) { +    const char *fmt_str = JITTemplate(ExpressionStrings(eExprElementType + i)); +    int written = snprintf(expr_bufs[i], jit_max_expr_size, fmt_str, context, +                           *elem.element_ptr.get()); +    if (written < 0) { +      if (log) +        log->Printf("%s - encoding error in snprintf().", __FUNCTION__); +      return false; +    } else if (written >= jit_max_expr_size) { +      if (log) +        log->Printf("%s - expression too long.", __FUNCTION__); +      return false; +    } + +    // Perform expression evaluation +    if (!EvalRSExpression(expr_bufs[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("%s - data type %" PRIu32 ", pixel type %" PRIu32 +                ", vector size %" PRIu32 ", field count %" PRIu32, +                __FUNCTION__, *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 +  return !(*elem.field_count.get() > 0 && +           !JITSubelements(elem, context, frame_ptr)); +} + +// 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("%s - failed to find allocation details.", __FUNCTION__); +    return false; +  } + +  const short num_exprs = 3; +  static_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 (uint32_t field_index = 0; field_index < field_count; ++field_index) { +    Element child; +    for (uint32_t expr_index = 0; expr_index < num_exprs; ++expr_index) { +      const char *fmt_str = +          JITTemplate(ExpressionStrings(eExprSubelementsId + expr_index)); +      int written = snprintf(expr_buffer, jit_max_expr_size, fmt_str, +                             context, field_count, field_count, field_count, +                             *elem.element_ptr.get(), field_count, field_index); +      if (written < 0) { +        if (log) +          log->Printf("%s - encoding error in snprintf().", __FUNCTION__); +        return false; +      } else if (written >= jit_max_expr_size) { +        if (log) +          log->Printf("%s - expression too long.", __FUNCTION__); +        return false; +      } + +      // Perform expression evaluation +      if (!EvalRSExpression(expr_buffer, frame_ptr, &results)) +        return false; + +      if (log) +        log->Printf("%s - expr result 0x%" PRIx64 ".", __FUNCTION__, results); + +      switch (expr_index) { +      case 0: // Element* of child +        child.element_ptr = static_cast<addr_t>(results); +        break; +      case 1: // Name of child +      { +        lldb::addr_t address = static_cast<addr_t>(results); +        Status err; +        std::string name; +        GetProcess()->ReadCStringFromMemory(address, name, err); +        if (!err.Fail()) +          child.type_name = ConstString(name); +        else { +          if (log) +            log->Printf("%s - warning: Couldn't read field name.", +                        __FUNCTION__); +        } +        break; +      } +      case 2: // Array size of child +        child.array_size = static_cast<uint32_t>(results); +        break; +      } +    } + +    // 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` parameter 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 *alloc, +                                            StackFrame *frame_ptr) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (!alloc->address.isValid() || !alloc->dimension.isValid() || +      !alloc->data_ptr.isValid() || !alloc->element.datum_size.isValid()) { +    if (log) +      log->Printf("%s - failed to find allocation details.", __FUNCTION__); +    return false; +  } + +  // Find dimensions +  uint32_t dim_x = alloc->dimension.get()->dim_1; +  uint32_t dim_y = alloc->dimension.get()->dim_2; +  uint32_t dim_z = alloc->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 (alloc->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; + +    alloc->size = dim_x * dim_y * dim_z * *alloc->element.datum_size.get(); + +    if (log) +      log->Printf("%s - inferred size of struct allocation %" PRIu32 ".", +                  __FUNCTION__, *alloc->size.get()); +    return true; +  } + +  const char *fmt_str = JITTemplate(eExprGetOffsetPtr); +  char expr_buf[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 written = snprintf(expr_buf, jit_max_expr_size, fmt_str, +                         *alloc->address.get(), dim_x, dim_y, dim_z); +  if (written < 0) { +    if (log) +      log->Printf("%s - encoding error in snprintf().", __FUNCTION__); +    return false; +  } else if (written >= jit_max_expr_size) { +    if (log) +      log->Printf("%s - expression too long.", __FUNCTION__); +    return false; +  } + +  uint64_t result = 0; +  if (!EvalRSExpression(expr_buf, 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 +  alloc->size = static_cast<uint32_t>(mem_ptr - *alloc->data_ptr.get()) + +                *alloc->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 *alloc, +                                              StackFrame *frame_ptr) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (!alloc->address.isValid() || !alloc->data_ptr.isValid()) { +    if (log) +      log->Printf("%s - failed to find allocation details.", __FUNCTION__); +    return false; +  } + +  const char *fmt_str = JITTemplate(eExprGetOffsetPtr); +  char expr_buf[jit_max_expr_size]; + +  int written = snprintf(expr_buf, jit_max_expr_size, fmt_str, +                         *alloc->address.get(), 0, 1, 0); +  if (written < 0) { +    if (log) +      log->Printf("%s - encoding error in snprintf().", __FUNCTION__); +    return false; +  } else if (written >= jit_max_expr_size) { +    if (log) +      log->Printf("%s - expression too long.", __FUNCTION__); +    return false; +  } + +  uint64_t result = 0; +  if (!EvalRSExpression(expr_buf, frame_ptr, &result)) +    return false; + +  addr_t mem_ptr = static_cast<lldb::addr_t>(result); +  alloc->stride = static_cast<uint32_t>(mem_ptr - *alloc->data_ptr.get()); + +  return true; +} + +// JIT all the current runtime info regarding an allocation +bool RenderScriptRuntime::RefreshAllocation(AllocationDetails *alloc, +                                            StackFrame *frame_ptr) { +  // GetOffsetPointer() +  if (!JITDataPointer(alloc, frame_ptr)) +    return false; + +  // rsaAllocationGetType() +  if (!JITTypePointer(alloc, frame_ptr)) +    return false; + +  // rsaTypeGetNativeData() +  if (!JITTypePacked(alloc, frame_ptr)) +    return false; + +  // rsaElementGetNativeData() +  if (!JITElementPacked(alloc->element, *alloc->context.get(), frame_ptr)) +    return false; + +  // Sets the datum_size member in Element +  SetElementSize(alloc->element); + +  // Use GetOffsetPointer() to infer size of the allocation +  return JITAllocationSize(alloc, frame_ptr); +} + +// 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 var_list; +  for (auto module_sp : m_rsmodules) +    module_sp->m_module->FindGlobalVariables( +        RegularExpression(llvm::StringRef(".")), UINT32_MAX, var_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 i = 0; i < var_list.GetSize(); ++i) { +    const VariableSP var_sp(var_list.GetVariableAtIndex(i)); +    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 i = 0; i < num_children; ++i) { +      ValueObjectSP child = valobj_sp->GetChildAtIndex(i, true); +      if (!child || (child->GetName() != elem.children[i].type_name)) { +        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 uint32_t size_diff = elem.children.size() - num_children; +      if (log) +        log->Printf("%s - %" PRIu32 " padding struct entries", __FUNCTION__, +                    size_diff); + +      for (uint32_t i = 0; i < size_diff; ++i) { +        ConstString name = elem.children[num_children + i].type_name; +        if (strcmp(name.AsCString(), "#rs_padding") < 0) +          found = false; +      } +    } + +    // We've found a global variable with matching type +    if (found) { +      // Dereference since our Element type isn't a pointer. +      if (valobj_sp->IsPointerType()) { +        Status 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("%s - element name set to %s", __FUNCTION__, +                    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 uint32_t vec_size = *elem.type_vec_size.get(); +  uint32_t data_size = 0; +  uint32_t 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 uint32_t array_size = +          child.array_size.isValid() ? *child.array_size.get() : 1; +      data_size += *child.datum_size.get() * array_size; +    } +  } +  // These have been packed already +  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) { +    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("%s - element size set to %" PRIu32, __FUNCTION__, +                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 *alloc, +                                       StackFrame *frame_ptr) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  // JIT all the allocation details +  if (alloc->ShouldRefresh()) { +    if (log) +      log->Printf("%s - allocation details not calculated yet, jitting info", +                  __FUNCTION__); + +    if (!RefreshAllocation(alloc, frame_ptr)) { +      if (log) +        log->Printf("%s - couldn't JIT allocation details", __FUNCTION__); +      return nullptr; +    } +  } + +  assert(alloc->data_ptr.isValid() && alloc->element.type.isValid() && +         alloc->element.type_vec_size.isValid() && alloc->size.isValid() && +         "Allocation information not available"); + +  // Allocate a buffer to copy data into +  const uint32_t size = *alloc->size.get(); +  std::shared_ptr<uint8_t> buffer(new uint8_t[size]); +  if (!buffer) { +    if (log) +      log->Printf("%s - couldn't allocate a %" PRIu32 " byte buffer", +                  __FUNCTION__, size); +    return nullptr; +  } + +  // Read the inferior memory +  Status err; +  lldb::addr_t data_ptr = *alloc->data_ptr.get(); +  GetProcess()->ReadMemory(data_ptr, buffer.get(), size, err); +  if (err.Fail()) { +    if (log) +      log->Printf("%s - '%s' Couldn't read %" PRIu32 +                  " bytes of allocation data from 0x%" PRIx64, +                  __FUNCTION__, err.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 +// incompatibilities +bool RenderScriptRuntime::LoadAllocation(Stream &strm, const uint32_t alloc_id, +                                         const char *path, +                                         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("%s - found allocation 0x%" PRIx64, __FUNCTION__, +                *alloc->address.get()); + +  // JIT all the allocation details +  if (alloc->ShouldRefresh()) { +    if (log) +      log->Printf("%s - allocation details not calculated yet, jitting info.", +                  __FUNCTION__); + +    if (!RefreshAllocation(alloc, frame_ptr)) { +      if (log) +        log->Printf("%s - couldn't JIT allocation details", __FUNCTION__); +      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(path); +  FileSystem::Instance().Resolve(file); +  if (!FileSystem::Instance().Exists(file)) { +    strm.Printf("Error: File %s does not exist", path); +    strm.EOL(); +    return false; +  } + +  if (!FileSystem::Instance().Readable(file)) { +    strm.Printf("Error: File %s does not have readable permissions", path); +    strm.EOL(); +    return false; +  } + +  // Read file into data buffer +  auto data_sp = FileSystem::Instance().CreateDataBuffer(file.GetPath()); + +  // Cast start of buffer to FileHeader and use pointer to read metadata +  void *file_buf = data_sp->GetBytes(); +  if (file_buf == nullptr || +      data_sp->GetByteSize() < (sizeof(AllocationDetails::FileHeader) + +                                sizeof(AllocationDetails::ElementHeader))) { +    strm.Printf("Error: File %s does not contain enough data for header", path); +    strm.EOL(); +    return false; +  } +  const AllocationDetails::FileHeader *file_header = +      static_cast<AllocationDetails::FileHeader *>(file_buf); + +  // Check file starts with ascii characters "RSAD" +  if (memcmp(file_header->ident, "RSAD", 4)) { +    strm.Printf("Error: File doesn't contain identifier for an RS allocation " +                "dump. Are you sure this is the correct file?"); +    strm.EOL(); +    return false; +  } + +  // Look at the type of the root element in the header +  AllocationDetails::ElementHeader root_el_hdr; +  memcpy(&root_el_hdr, static_cast<uint8_t *>(file_buf) + +                           sizeof(AllocationDetails::FileHeader), +         sizeof(AllocationDetails::ElementHeader)); + +  if (log) +    log->Printf("%s - header type %" PRIu32 ", element size %" PRIu32, +                __FUNCTION__, root_el_hdr.type, root_el_hdr.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() != root_el_hdr.element_size) { +    strm.Printf("Warning: Mismatched Element sizes - file %" PRIu32 +                " bytes, allocation %" PRIu32 " bytes", +                root_el_hdr.element_size, *alloc->element.datum_size.get()); +    strm.EOL(); +  } + +  // Check if the target allocation and file both have the same type +  const uint32_t alloc_type = static_cast<uint32_t>(*alloc->element.type.get()); +  const uint32_t file_type = root_el_hdr.type; + +  if (file_type > Element::RS_TYPE_FONT) { +    strm.Printf("Warning: File has unknown allocation type"); +    strm.EOL(); +  } else if (alloc_type != file_type) { +    // Enum value isn't monotonous, so doesn't always index RsDataTypeToString +    // array +    uint32_t target_type_name_idx = alloc_type; +    uint32_t head_type_name_idx = file_type; +    if (alloc_type >= Element::RS_TYPE_ELEMENT && +        alloc_type <= Element::RS_TYPE_FONT) +      target_type_name_idx = static_cast<Element::DataType>( +          (alloc_type - Element::RS_TYPE_ELEMENT) + +          Element::RS_TYPE_MATRIX_2X2 + 1); + +    if (file_type >= Element::RS_TYPE_ELEMENT && +        file_type <= Element::RS_TYPE_FONT) +      head_type_name_idx = static_cast<Element::DataType>( +          (file_type - Element::RS_TYPE_ELEMENT) + Element::RS_TYPE_MATRIX_2X2 + +          1); + +    const char *head_type_name = +        AllocationDetails::RsDataTypeToString[head_type_name_idx][0]; +    const char *target_type_name = +        AllocationDetails::RsDataTypeToString[target_type_name_idx][0]; + +    strm.Printf( +        "Warning: Mismatched Types - file '%s' type, allocation '%s' type", +        head_type_name, target_type_name); +    strm.EOL(); +  } + +  // Advance buffer past header +  file_buf = static_cast<uint8_t *>(file_buf) + file_header->hdr_size; + +  // Calculate size of allocation data in file +  size_t size = data_sp->GetByteSize() - file_header->hdr_size; + +  // Check if the target allocation and file both have the same total data +  // size. +  const uint32_t alloc_size = *alloc->size.get(); +  if (alloc_size != size) { +    strm.Printf("Warning: Mismatched allocation sizes - file 0x%" PRIx64 +                " bytes, allocation 0x%" PRIx32 " bytes", +                (uint64_t)size, alloc_size); +    strm.EOL(); +    // Set length to copy to minimum +    size = alloc_size < size ? alloc_size : size; +  } + +  // Copy file data from our buffer into the target allocation. +  lldb::addr_t alloc_data = *alloc->data_ptr.get(); +  Status err; +  size_t written = GetProcess()->WriteMemory(alloc_data, file_buf, size, err); +  if (!err.Success() || written != size) { +    strm.Printf("Error: Couldn't write data to allocation %s", err.AsCString()); +    strm.EOL(); +    return false; +  } + +  strm.Printf("Contents of file '%s' read into allocation %" PRIu32, path, +              alloc->id); +  strm.EOL(); + +  return true; +} + +// Function takes as parameters a byte buffer, which will eventually be written +// to file as the element header, an offset into that buffer, and an Element +// that will be saved into the buffer at the parametrised offset. Return value +// is the new offset after writing the element into the buffer. Elements are +// saved to the file as the ElementHeader struct followed by offsets to the +// structs of all the element's children. +size_t RenderScriptRuntime::PopulateElementHeaders( +    const std::shared_ptr<uint8_t> header_buffer, size_t offset, +    const Element &elem) { +  // File struct for an element header with all the relevant details copied +  // from elem. We assume members are valid already. +  AllocationDetails::ElementHeader elem_header; +  elem_header.type = *elem.type.get(); +  elem_header.kind = *elem.type_kind.get(); +  elem_header.element_size = *elem.datum_size.get(); +  elem_header.vector_size = *elem.type_vec_size.get(); +  elem_header.array_size = +      elem.array_size.isValid() ? *elem.array_size.get() : 0; +  const size_t elem_header_size = sizeof(AllocationDetails::ElementHeader); + +  // Copy struct into buffer and advance offset We assume that header_buffer +  // has been checked for nullptr before this method is called +  memcpy(header_buffer.get() + offset, &elem_header, elem_header_size); +  offset += elem_header_size; + +  // Starting offset of child ElementHeader struct +  size_t child_offset = +      offset + ((elem.children.size() + 1) * sizeof(uint32_t)); +  for (const RenderScriptRuntime::Element &child : elem.children) { +    // Recursively populate the buffer with the element header structs of +    // children. Then save the offsets where they were set after the parent +    // element header. +    memcpy(header_buffer.get() + offset, &child_offset, sizeof(uint32_t)); +    offset += sizeof(uint32_t); + +    child_offset = PopulateElementHeaders(header_buffer, child_offset, child); +  } + +  // Zero indicates no more children +  memset(header_buffer.get() + offset, 0, sizeof(uint32_t)); + +  return child_offset; +} + +// Given an Element object this function returns the total size needed in the +// file header to store the element's details. Taking into account the size of +// the element header struct, plus the offsets to all the element's children. +// Function is recursive so that the size of all ancestors is taken into +// account. +size_t RenderScriptRuntime::CalculateElementHeaderSize(const Element &elem) { +  // Offsets to children plus zero terminator +  size_t size = (elem.children.size() + 1) * sizeof(uint32_t); +  // Size of header struct with type details +  size += sizeof(AllocationDetails::ElementHeader); + +  // Calculate recursively for all descendants +  for (const Element &child : elem.children) +    size += CalculateElementHeaderSize(child); + +  return size; +} + +// 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 *path, +                                         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("%s - found allocation 0x%" PRIx64 ".", __FUNCTION__, +                *alloc->address.get()); + +  // JIT all the allocation details +  if (alloc->ShouldRefresh()) { +    if (log) +      log->Printf("%s - allocation details not calculated yet, jitting info.", +                  __FUNCTION__); + +    if (!RefreshAllocation(alloc, frame_ptr)) { +      if (log) +        log->Printf("%s - couldn't JIT allocation details.", __FUNCTION__); +      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(path); +  FileSystem::Instance().Resolve(file_spec); +  File file; +  FileSystem::Instance().Open(file, file_spec, +                              File::eOpenOptionWrite | +                                  File::eOpenOptionCanCreate | +                                  File::eOpenOptionTruncate); + +  if (!file) { +    strm.Printf("Error: Failed to open '%s' for writing", path); +    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; +  memcpy(head.ident, "RSAD", 4); +  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); + +  const size_t element_header_size = CalculateElementHeaderSize(alloc->element); +  assert((sizeof(AllocationDetails::FileHeader) + element_header_size) < +             UINT16_MAX && +         "Element header too large"); +  head.hdr_size = static_cast<uint16_t>(sizeof(AllocationDetails::FileHeader) + +                                        element_header_size); + +  // Write the file header +  size_t num_bytes = sizeof(AllocationDetails::FileHeader); +  if (log) +    log->Printf("%s - writing File Header, 0x%" PRIx64 " bytes", __FUNCTION__, +                (uint64_t)num_bytes); + +  Status err = file.Write(&head, num_bytes); +  if (!err.Success()) { +    strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path); +    strm.EOL(); +    return false; +  } + +  // Create the headers describing the element type of the allocation. +  std::shared_ptr<uint8_t> element_header_buffer( +      new uint8_t[element_header_size]); +  if (element_header_buffer == nullptr) { +    strm.Printf("Internal Error: Couldn't allocate %" PRIu64 +                " bytes on the heap", +                (uint64_t)element_header_size); +    strm.EOL(); +    return false; +  } + +  PopulateElementHeaders(element_header_buffer, 0, alloc->element); + +  // Write headers for allocation element type to file +  num_bytes = element_header_size; +  if (log) +    log->Printf("%s - writing element headers, 0x%" PRIx64 " bytes.", +                __FUNCTION__, (uint64_t)num_bytes); + +  err = file.Write(element_header_buffer.get(), num_bytes); +  if (!err.Success()) { +    strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path); +    strm.EOL(); +    return false; +  } + +  // Write allocation data to file +  num_bytes = static_cast<size_t>(*alloc->size.get()); +  if (log) +    log->Printf("%s - writing 0x%" PRIx64 " bytes", __FUNCTION__, +                (uint64_t)num_bytes); + +  err = file.Write(buffer.get(), num_bytes); +  if (!err.Success()) { +    strm.Printf("Error: '%s' when writing to file '%s'", err.AsCString(), path); +    strm.EOL(); +    return false; +  } + +  strm.Printf("Allocation written to file '%s'", path); +  strm.EOL(); +  return true; +} + +bool RenderScriptRuntime::LoadModule(const lldb::ModuleSP &module_sp) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (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)) { +    case eModuleKindKernelObj: { +      RSModuleDescriptorSP module_desc; +      module_desc = std::make_shared<RSModuleDescriptor>(module_sp); +      if (module_desc->ParseRSInfo()) { +        m_rsmodules.push_back(module_desc); +        module_desc->WarnIfVersionMismatch(GetProcess() +                                               ->GetTarget() +                                               .GetDebugger() +                                               .GetAsyncOutputStream() +                                               .get()); +        module_loaded = true; +      } +      if (module_loaded) { +        FixupScriptDetails(module_desc); +      } +      break; +    } +    case eModuleKindDriver: { +      if (!m_libRSDriver) { +        m_libRSDriver = module_sp; +        LoadRuntimeHooks(m_libRSDriver, RenderScriptRuntime::eModuleKindDriver); +      } +      break; +    } +    case eModuleKindImpl: { +      if (!m_libRSCpuRef) { +        m_libRSCpuRef = module_sp; +        LoadRuntimeHooks(m_libRSCpuRef, RenderScriptRuntime::eModuleKindImpl); +      } +      break; +    } +    case eModuleKindLibRS: { +      if (!m_libRS) { +        m_libRS = module_sp; +        static ConstString gDbgPresentStr("gDebuggerPresent"); +        const Symbol *debug_present = m_libRS->FindFirstSymbolWithNameAndType( +            gDbgPresentStr, eSymbolTypeData); +        if (debug_present) { +          Status err; +          uint32_t flag = 0x00000001U; +          Target &target = GetProcess()->GetTarget(); +          addr_t addr = debug_present->GetLoadAddress(&target); +          GetProcess()->WriteMemory(addr, &flag, sizeof(flag), err); +          if (err.Success()) { +            if (log) +              log->Printf("%s - debugger present flag set on debugee.", +                          __FUNCTION__); + +            m_debuggerPresentFlagged = true; +          } else if (log) { +            log->Printf("%s - error writing debugger present flags '%s' ", +                        __FUNCTION__, err.AsCString()); +          } +        } else if (log) { +          log->Printf( +              "%s - error writing debugger present flags - symbol not found", +              __FUNCTION__); +        } +      } +      break; +    } +    default: +      break; +    } +    if (module_loaded) +      Update(); +    return module_loaded; +  } +  return false; +} + +void RenderScriptRuntime::Update() { +  if (m_rsmodules.size() > 0) { +    if (!m_initiated) { +      Initiate(); +    } +  } +} + +void RSModuleDescriptor::WarnIfVersionMismatch(lldb_private::Stream *s) const { +  if (!s) +    return; + +  if (m_slang_version.empty() || m_bcc_version.empty()) { +    s->PutCString("WARNING: Unknown bcc or slang (llvm-rs-cc) version; debug " +                  "experience may be unreliable"); +    s->EOL(); +  } else if (m_slang_version != m_bcc_version) { +    s->Printf("WARNING: The debug info emitted by the slang frontend " +              "(llvm-rs-cc) used to build this module (%s) does not match the " +              "version of bcc used to generate the debug information (%s). " +              "This is an unsupported configuration and may result in a poor " +              "debugging experience; proceed with caution", +              m_slang_version.c_str(), m_bcc_version.c_str()); +    s->EOL(); +  } +} + +bool RSModuleDescriptor::ParsePragmaCount(llvm::StringRef *lines, +                                          size_t n_lines) { +  // Skip the pragma prototype line +  ++lines; +  for (; n_lines--; ++lines) { +    const auto kv_pair = lines->split(" - "); +    m_pragmas[kv_pair.first.trim().str()] = kv_pair.second.trim().str(); +  } +  return true; +} + +bool RSModuleDescriptor::ParseExportReduceCount(llvm::StringRef *lines, +                                                size_t n_lines) { +  // The list of reduction kernels in the `.rs.info` symbol is of the form +  // "signature - accumulatordatasize - reduction_name - initializer_name - +  // accumulator_name - combiner_name - outconverter_name - halter_name" Where +  // a function is not explicitly named by the user, or is not generated by the +  // compiler, it is named "." so the dash separated list should always be 8 +  // items long +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); +  // Skip the exportReduceCount line +  ++lines; +  for (; n_lines--; ++lines) { +    llvm::SmallVector<llvm::StringRef, 8> spec; +    lines->split(spec, " - "); +    if (spec.size() != 8) { +      if (spec.size() < 8) { +        if (log) +          log->Error("Error parsing RenderScript reduction spec. wrong number " +                     "of fields"); +        return false; +      } else if (log) +        log->Warning("Extraneous members in reduction spec: '%s'", +                     lines->str().c_str()); +    } + +    const auto sig_s = spec[0]; +    uint32_t sig; +    if (sig_s.getAsInteger(10, sig)) { +      if (log) +        log->Error("Error parsing Renderscript reduction spec: invalid kernel " +                   "signature: '%s'", +                   sig_s.str().c_str()); +      return false; +    } + +    const auto accum_data_size_s = spec[1]; +    uint32_t accum_data_size; +    if (accum_data_size_s.getAsInteger(10, accum_data_size)) { +      if (log) +        log->Error("Error parsing Renderscript reduction spec: invalid " +                   "accumulator data size %s", +                   accum_data_size_s.str().c_str()); +      return false; +    } + +    if (log) +      log->Printf("Found RenderScript reduction '%s'", spec[2].str().c_str()); + +    m_reductions.push_back(RSReductionDescriptor(this, sig, accum_data_size, +                                                 spec[2], spec[3], spec[4], +                                                 spec[5], spec[6], spec[7])); +  } +  return true; +} + +bool RSModuleDescriptor::ParseVersionInfo(llvm::StringRef *lines, +                                          size_t n_lines) { +  // Skip the versionInfo line +  ++lines; +  for (; n_lines--; ++lines) { +    // We're only interested in bcc and slang versions, and ignore all other +    // versionInfo lines +    const auto kv_pair = lines->split(" - "); +    if (kv_pair.first == "slang") +      m_slang_version = kv_pair.second.str(); +    else if (kv_pair.first == "bcc") +      m_bcc_version = kv_pair.second.str(); +  } +  return true; +} + +bool RSModuleDescriptor::ParseExportForeachCount(llvm::StringRef *lines, +                                                 size_t n_lines) { +  // Skip the exportForeachCount line +  ++lines; +  for (; n_lines--; ++lines) { +    uint32_t slot; +    // `forEach` kernels are listed in the `.rs.info` packet as a "slot - name" +    // pair per line +    const auto kv_pair = lines->split(" - "); +    if (kv_pair.first.getAsInteger(10, slot)) +      return false; +    m_kernels.push_back(RSKernelDescriptor(this, kv_pair.second, slot)); +  } +  return true; +} + +bool RSModuleDescriptor::ParseExportVarCount(llvm::StringRef *lines, +                                             size_t n_lines) { +  // Skip the ExportVarCount line +  ++lines; +  for (; n_lines--; ++lines) +    m_globals.push_back(RSGlobalDescriptor(this, *lines)); +  return true; +} + +// The .rs.info symbol in renderscript modules contains a string which needs to +// be parsed. The string is basic and is parsed on a line by line basis. +bool RSModuleDescriptor::ParseRSInfo() { +  assert(m_module); +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); +  const Symbol *info_sym = m_module->FindFirstSymbolWithNameAndType( +      ConstString(".rs.info"), eSymbolTypeData); +  if (!info_sym) +    return false; + +  const addr_t addr = info_sym->GetAddressRef().GetFileAddress(); +  if (addr == LLDB_INVALID_ADDRESS) +    return false; + +  const addr_t size = info_sym->GetByteSize(); +  const FileSpec fs = m_module->GetFileSpec(); + +  auto buffer = +      FileSystem::Instance().CreateDataBuffer(fs.GetPath(), size, addr); +  if (!buffer) +    return false; + +  // split rs.info. contents into lines +  llvm::SmallVector<llvm::StringRef, 128> info_lines; +  { +    const llvm::StringRef raw_rs_info((const char *)buffer->GetBytes()); +    raw_rs_info.split(info_lines, '\n'); +    if (log) +      log->Printf("'.rs.info symbol for '%s':\n%s", +                  m_module->GetFileSpec().GetCString(), +                  raw_rs_info.str().c_str()); +  } + +  enum { +    eExportVar, +    eExportForEach, +    eExportReduce, +    ePragma, +    eBuildChecksum, +    eObjectSlot, +    eVersionInfo, +  }; + +  const auto rs_info_handler = [](llvm::StringRef name) -> int { +    return llvm::StringSwitch<int>(name) +        // The number of visible global variables in the script +        .Case("exportVarCount", eExportVar) +        // The number of RenderScrip `forEach` kernels __attribute__((kernel)) +        .Case("exportForEachCount", eExportForEach) +        // The number of generalreductions: This marked in the script by +        // `#pragma reduce()` +        .Case("exportReduceCount", eExportReduce) +        // Total count of all RenderScript specific `#pragmas` used in the +        // script +        .Case("pragmaCount", ePragma) +        .Case("objectSlotCount", eObjectSlot) +        .Case("versionInfo", eVersionInfo) +        .Default(-1); +  }; + +  // parse all text lines of .rs.info +  for (auto line = info_lines.begin(); line != info_lines.end(); ++line) { +    const auto kv_pair = line->split(": "); +    const auto key = kv_pair.first; +    const auto val = kv_pair.second.trim(); + +    const auto handler = rs_info_handler(key); +    if (handler == -1) +      continue; +    // getAsInteger returns `true` on an error condition - we're only +    // interested in numeric fields at the moment +    uint64_t n_lines; +    if (val.getAsInteger(10, n_lines)) { +      LLDB_LOGV(log, "Failed to parse non-numeric '.rs.info' section {0}", +                line->str()); +      continue; +    } +    if (info_lines.end() - (line + 1) < (ptrdiff_t)n_lines) +      return false; + +    bool success = false; +    switch (handler) { +    case eExportVar: +      success = ParseExportVarCount(line, n_lines); +      break; +    case eExportForEach: +      success = ParseExportForeachCount(line, n_lines); +      break; +    case eExportReduce: +      success = ParseExportReduceCount(line, n_lines); +      break; +    case ePragma: +      success = ParsePragmaCount(line, n_lines); +      break; +    case eVersionInfo: +      success = ParseVersionInfo(line, n_lines); +      break; +    default: { +      if (log) +        log->Printf("%s - skipping .rs.info field '%s'", __FUNCTION__, +                    line->str().c_str()); +      continue; +    } +    } +    if (!success) +      return false; +    line += n_lines; +  } +  return info_lines.size() > 0; +} + +void RenderScriptRuntime::DumpStatus(Stream &strm) const { +  if (m_libRS) { +    strm.Printf("Runtime Library discovered."); +    strm.EOL(); +  } +  if (m_libRSDriver) { +    strm.Printf("Runtime Driver discovered."); +    strm.EOL(); +  } +  if (m_libRSCpuRef) { +    strm.Printf("CPU Reference Implementation discovered."); +    strm.EOL(); +  } + +  if (m_runtimeHooks.size()) { +    strm.Printf("Runtime functions hooked:"); +    strm.EOL(); +    for (auto b : m_runtimeHooks) { +      strm.Indent(b.second->defn->name); +      strm.EOL(); +    } +  } else { +    strm.Printf("Runtime is not hooked."); +    strm.EOL(); +  } +} + +void RenderScriptRuntime::DumpContexts(Stream &strm) const { +  strm.Printf("Inferred RenderScript Contexts:"); +  strm.EOL(); +  strm.IndentMore(); + +  std::map<addr_t, uint64_t> contextReferences; + +  // 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 (!script->context.isValid()) +      continue; +    lldb::addr_t context = *script->context; + +    if (contextReferences.find(context) != contextReferences.end()) { +      contextReferences[context]++; +    } else { +      contextReferences[context] = 1; +    } +  } + +  for (const auto &cRef : contextReferences) { +    strm.Printf("Context 0x%" PRIx64 ": %" PRIu64 " script instances", +                cRef.first, cRef.second); +    strm.EOL(); +  } +  strm.IndentLess(); +} + +void RenderScriptRuntime::DumpKernels(Stream &strm) const { +  strm.Printf("RenderScript Kernels:"); +  strm.EOL(); +  strm.IndentMore(); +  for (const auto &module : m_rsmodules) { +    strm.Printf("Resource '%s':", module->m_resname.c_str()); +    strm.EOL(); +    for (const auto &kernel : module->m_kernels) { +      strm.Indent(kernel.m_name.AsCString()); +      strm.EOL(); +    } +  } +  strm.IndentLess(); +} + +RenderScriptRuntime::AllocationDetails * +RenderScriptRuntime::FindAllocByID(Stream &strm, const uint32_t alloc_id) { +  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) { +    alloc = m_allocations[alloc_id - 1].get(); +    return alloc; +  } + +  // Fallback to searching +  for (const auto &a : m_allocations) { +    if (a->id == alloc_id) { +      alloc = a.get(); +      break; +    } +  } + +  if (alloc == nullptr) { +    strm.Printf("Error: Couldn't find allocation with id matching %" PRIu32, +                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("%s - found allocation 0x%" PRIx64, __FUNCTION__, +                *alloc->address.get()); + +  // Check we have information about the allocation, if not calculate it +  if (alloc->ShouldRefresh()) { +    if (log) +      log->Printf("%s - allocation details not calculated yet, jitting info.", +                  __FUNCTION__); + +    // JIT all the allocation information +    if (!RefreshAllocation(alloc, frame_ptr)) { +      strm.Printf("Error: Couldn't JIT allocation details"); +      strm.EOL(); +      return false; +    } +  } + +  // Establish format and size of each data element +  const uint32_t 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 uint32_t data_size = *alloc->element.datum_size.get(); + +  if (log) +    log->Printf("%s - element size %" PRIu32 " bytes, including padding", +                __FUNCTION__, 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 uint32_t stride = *alloc->stride.get(); +  const uint32_t size = *alloc->size.get(); // Size of whole allocation +  const uint32_t padding = +      alloc->element.padding.isValid() ? *alloc->element.padding.get() : 0; +  if (log) +    log->Printf("%s - stride %" PRIu32 " bytes, size %" PRIu32 +                " bytes, padding %" PRIu32, +                __FUNCTION__, stride, size, padding); + +  // Find dimensions used to index loops, so need to be non-zero +  uint32_t dim_x = alloc->dimension.get()->dim_1; +  dim_x = dim_x == 0 ? 1 : dim_x; + +  uint32_t dim_y = alloc->dimension.get()->dim_2; +  dim_y = dim_y == 0 ? 1 : dim_y; + +  uint32_t dim_z = alloc->dimension.get()->dim_3; +  dim_z = dim_z == 0 ? 1 : dim_z; + +  // Use data extractor to format output +  const uint32_t target_ptr_size = +      GetProcess()->GetTarget().GetArchitecture().GetAddressByteSize(); +  DataExtractor alloc_data(buffer.get(), size, GetProcess()->GetByteOrder(), +                           target_ptr_size); + +  uint32_t offset = 0;   // Offset in buffer to next element to be printed +  uint32_t 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 (uint32_t z = 0; z < dim_z; ++z) { +    for (uint32_t 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 (uint32_t x = 0; x < dim_x; ++x) { +        strm.Printf("\n(%" PRIu32 ", %" PRIu32 ", %" PRIu32 ") = ", x, y, z); +        if ((type == Element::RS_TYPE_NONE) && +            (alloc->element.children.size() > 0) && +            (alloc->element.type_name != Element::GetFallbackStructName())) { +          // 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. 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 dereferencing a pointer cast to element +          // address. +          char expr_char_buffer[jit_max_expr_size]; +          int written = +              snprintf(expr_char_buffer, jit_max_expr_size, "*(%s*) 0x%" PRIx64, +                       alloc->element.type_name.AsCString(), +                       *alloc->data_ptr.get() + offset); + +          if (written < 0 || written >= jit_max_expr_size) { +            if (log) +              log->Printf("%s - error in snprintf().", __FUNCTION__); +            continue; +          } + +          // 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 { +          DumpDataExtractor(alloc_data, &strm, offset, format, +                            data_size - padding, 1, 1, LLDB_INVALID_ADDRESS, 0, +                            0); +        } +        offset += data_size; +      } +    } +  } +  strm.EOL(); + +  return true; +} + +// Function recalculates all our cached information about allocations by +// jitting the RS runtime regarding each allocation we know about. Returns true +// if all allocations could be recomputed, false otherwise. +bool RenderScriptRuntime::RecomputeAllAllocations(Stream &strm, +                                                  StackFrame *frame_ptr) { +  bool success = true; +  for (auto &alloc : m_allocations) { +    // JIT current allocation information +    if (!RefreshAllocation(alloc.get(), frame_ptr)) { +      strm.Printf("Error: Couldn't evaluate details for allocation %" PRIu32 +                  "\n", +                  alloc->id); +      success = false; +    } +  } + +  if (success) +    strm.Printf("All allocations successfully recomputed"); +  strm.EOL(); + +  return success; +} + +// Prints information regarding currently loaded allocations. These details are +// gathered by jitting the runtime, which has as latency. Index parameter +// specifies a single allocation ID to print, or a zero value to print them all +void RenderScriptRuntime::ListAllocations(Stream &strm, StackFrame *frame_ptr, +                                          const uint32_t index) { +  strm.Printf("RenderScript Allocations:"); +  strm.EOL(); +  strm.IndentMore(); + +  for (auto &alloc : m_allocations) { +    // index will only be zero if we want to print all allocations +    if (index != 0 && index != alloc->id) +      continue; + +    // JIT current allocation information +    if (alloc->ShouldRefresh() && !RefreshAllocation(alloc.get(), frame_ptr)) { +      strm.Printf("Error: Couldn't evaluate details for allocation %" PRIu32, +                  alloc->id); +      strm.EOL(); +      continue; +    } + +    strm.Printf("%" PRIu32 ":", alloc->id); +    strm.EOL(); +    strm.IndentMore(); + +    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("(%" PRId32 ", %" PRId32 ", %" PRId32 ")\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<uint32_t>(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<uint32_t>(kind)]); +    } + +    strm.EOL(); +    strm.IndentLess(); +  } +  strm.IndentLess(); +} + +// Set breakpoints on every kernel found in RS module +void RenderScriptRuntime::BreakOnModuleKernels( +    const RSModuleDescriptorSP rsmodule_sp) { +  for (const auto &kernel : rsmodule_sp->m_kernels) { +    // Don't set breakpoint on 'root' kernel +    if (strcmp(kernel.m_name.AsCString(), "root") == 0) +      continue; + +    CreateKernelBreakpoint(kernel.m_name); +  } +} + +// 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("%s(True) - breakpoints set on all currently loaded kernels.", +                  __FUNCTION__); +  } else if (!do_break && +             m_breakAllKernels) // Breakpoints won't be set on any new kernels. +  { +    m_breakAllKernels = false; + +    if (log) +      log->Printf("%s(False) - breakpoints no longer automatically set.", +                  __FUNCTION__); +  } +} + +// 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(ConstString name) { +  Log *log( +      GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); + +  if (!m_filtersp) { +    if (log) +      log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__); +    return nullptr; +  } + +  BreakpointResolverSP resolver_sp(new RSBreakpointResolver(nullptr, name)); +  Target &target = GetProcess()->GetTarget(); +  BreakpointSP bp = target.CreateBreakpoint( +      m_filtersp, resolver_sp, false, false, false); + +  // Give RS breakpoints a specific name, so the user can manipulate them as a +  // group. +  Status err; +  target.AddNameToBreakpoint(bp, "RenderScriptKernel", err); +  if (err.Fail() && log) +    if (log) +      log->Printf("%s - error setting break name, '%s'.", __FUNCTION__, +                  err.AsCString()); + +  return bp; +} + +BreakpointSP +RenderScriptRuntime::CreateReductionBreakpoint(ConstString name, +                                               int kernel_types) { +  Log *log( +      GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); + +  if (!m_filtersp) { +    if (log) +      log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__); +    return nullptr; +  } + +  BreakpointResolverSP resolver_sp(new RSReduceBreakpointResolver( +      nullptr, name, &m_rsmodules, kernel_types)); +  Target &target = GetProcess()->GetTarget(); +  BreakpointSP bp = target.CreateBreakpoint( +      m_filtersp, resolver_sp, false, false, false); + +  // Give RS breakpoints a specific name, so the user can manipulate them as a +  // group. +  Status err; +  target.AddNameToBreakpoint(bp, "RenderScriptReduction", err); +  if (err.Fail() && log) +      log->Printf("%s - error setting break name, '%s'.", __FUNCTION__, +                  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)); +  Status err; +  VariableSP var_sp; + +  // Find variable in stack frame +  ValueObjectSP value_sp(frame_sp->GetValueForVariableExpressionPath( +      var_name, eNoDynamicValues, +      StackFrame::eExpressionPathOptionCheckPtrVsMember | +          StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, +      var_sp, err)); +  if (!err.Success()) { +    if (log) +      log->Printf("%s - error, couldn't find '%s' in frame", __FUNCTION__, +                  var_name); +    return false; +  } + +  // Find the uint32_t value for the variable +  bool success = false; +  val = value_sp->GetValueAsUnsigned(0, &success); +  if (!success) { +    if (log) +      log->Printf("%s - error, couldn't parse '%s' as an uint32_t.", +                  __FUNCTION__, var_name); +    return false; +  } + +  return true; +} + +// Function attempts to find the current coordinate of a kernel invocation by +// investigating the values of frame variables in the .expand function. These +// coordinates are returned via the coord array reference parameter. Returns +// true if the coordinates could be found, and false otherwise. +bool RenderScriptRuntime::GetKernelCoordinate(RSCoordinate &coord, +                                              Thread *thread_ptr) { +  static const char *const x_expr = "rsIndex"; +  static const char *const y_expr = "p->current.y"; +  static const char *const z_expr = "p->current.z"; + +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + +  if (!thread_ptr) { +    if (log) +      log->Printf("%s - Error, No thread pointer", __FUNCTION__); + +    return false; +  } + +  // Walk the call stack looking for a function whose name has the suffix +  // '.expand' and contains the variables we're looking for. +  for (uint32_t i = 0; i < thread_ptr->GetStackFrameCount(); ++i) { +    if (!thread_ptr->SetSelectedFrameByIndex(i)) +      continue; + +    StackFrameSP frame_sp = thread_ptr->GetSelectedFrame(); +    if (!frame_sp) +      continue; + +    // Find the function name +    const SymbolContext sym_ctx = +        frame_sp->GetSymbolContext(eSymbolContextFunction); +    const ConstString func_name = sym_ctx.GetFunctionName(); +    if (!func_name) +      continue; + +    if (log) +      log->Printf("%s - Inspecting function '%s'", __FUNCTION__, +                  func_name.GetCString()); + +    // Check if function name has .expand suffix +    if (!func_name.GetStringRef().endswith(".expand")) +      continue; + +    if (log) +      log->Printf("%s - Found .expand function '%s'", __FUNCTION__, +                  func_name.GetCString()); + +    // Get values for variables in .expand frame that tell us the current +    // kernel invocation +    uint64_t x, y, z; +    bool found = GetFrameVarAsUnsigned(frame_sp, x_expr, x) && +                 GetFrameVarAsUnsigned(frame_sp, y_expr, y) && +                 GetFrameVarAsUnsigned(frame_sp, z_expr, z); + +    if (found) { +      // The RenderScript runtime uses uint32_t for these vars. If they're not +      // within bounds, our frame parsing is garbage +      assert(x <= UINT32_MAX && y <= UINT32_MAX && z <= UINT32_MAX); +      coord.x = (uint32_t)x; +      coord.y = (uint32_t)y; +      coord.z = (uint32_t)z; +      return true; +    } +  } +  return false; +} + +// 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 +  RSCoordinate target_coord = *static_cast<RSCoordinate *>(baton); + +  if (log) +    log->Printf("%s - Break ID %" PRIu64 ", " FMT_COORD, __FUNCTION__, break_id, +                target_coord.x, target_coord.y, target_coord.z); + +  // Select current thread +  ExecutionContext context(ctx->exe_ctx_ref); +  Thread *thread_ptr = context.GetThreadPtr(); +  assert(thread_ptr && "Null thread pointer"); + +  // Find current kernel invocation from .expand frame variables +  RSCoordinate current_coord{}; +  if (!GetKernelCoordinate(current_coord, thread_ptr)) { +    if (log) +      log->Printf("%s - Error, couldn't select .expand stack frame", +                  __FUNCTION__); +    return false; +  } + +  if (log) +    log->Printf("%s - " FMT_COORD, __FUNCTION__, current_coord.x, +                current_coord.y, current_coord.z); + +  // Check if the current kernel invocation coordinate matches our target +  // coordinate +  if (target_coord == current_coord) { +    if (log) +      log->Printf("%s, BREAKING " FMT_COORD, __FUNCTION__, current_coord.x, +                  current_coord.y, current_coord.z); + +    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; +} + +void RenderScriptRuntime::SetConditional(BreakpointSP bp, Stream &messages, +                                         const RSCoordinate &coord) { +  messages.Printf("Conditional kernel breakpoint on coordinate " FMT_COORD, +                  coord.x, coord.y, coord.z); +  messages.EOL(); + +  // Allocate memory for the baton, and copy over coordinate +  RSCoordinate *baton = new RSCoordinate(coord); + +  // Create a callback that will be invoked every time 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::unique_ptr<RSCoordinate>(baton); +} + +// 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. +bool RenderScriptRuntime::PlaceBreakpointOnKernel(TargetSP target, +                                                  Stream &messages, +                                                  const char *name, +                                                  const RSCoordinate *coord) { +  if (!name) +    return false; + +  InitSearchFilter(target); + +  ConstString kernel_name(name); +  BreakpointSP bp = CreateKernelBreakpoint(kernel_name); +  if (!bp) +    return false; + +  // We have a conditional breakpoint on a specific coordinate +  if (coord) +    SetConditional(bp, messages, *coord); + +  bp->GetDescription(&messages, lldb::eDescriptionLevelInitial, false); + +  return true; +} + +BreakpointSP +RenderScriptRuntime::CreateScriptGroupBreakpoint(ConstString name, +                                                 bool stop_on_all) { +  Log *log( +      GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); + +  if (!m_filtersp) { +    if (log) +      log->Printf("%s - error, no breakpoint search filter set.", __FUNCTION__); +    return nullptr; +  } + +  BreakpointResolverSP resolver_sp(new RSScriptGroupBreakpointResolver( +      nullptr, name, m_scriptGroups, stop_on_all)); +  Target &target = GetProcess()->GetTarget(); +  BreakpointSP bp = target.CreateBreakpoint( +      m_filtersp, resolver_sp, false, false, false); +  // Give RS breakpoints a specific name, so the user can manipulate them as a +  // group. +  Status err; +  target.AddNameToBreakpoint(bp, name.GetCString(), err); +  if (err.Fail() && log) +    log->Printf("%s - error setting break name, '%s'.", __FUNCTION__, +                err.AsCString()); +  // ask the breakpoint to resolve itself +  bp->ResolveBreakpoint(); +  return bp; +} + +bool RenderScriptRuntime::PlaceBreakpointOnScriptGroup(TargetSP target, +                                                       Stream &strm, +                                                       ConstString name, +                                                       bool multi) { +  InitSearchFilter(target); +  BreakpointSP bp = CreateScriptGroupBreakpoint(name, multi); +  if (bp) +    bp->GetDescription(&strm, lldb::eDescriptionLevelInitial, false); +  return bool(bp); +} + +bool RenderScriptRuntime::PlaceBreakpointOnReduction(TargetSP target, +                                                     Stream &messages, +                                                     const char *reduce_name, +                                                     const RSCoordinate *coord, +                                                     int kernel_types) { +  if (!reduce_name) +    return false; + +  InitSearchFilter(target); +  BreakpointSP bp = +      CreateReductionBreakpoint(ConstString(reduce_name), kernel_types); +  if (!bp) +    return false; + +  if (coord) +    SetConditional(bp, messages, *coord); + +  bp->GetDescription(&messages, lldb::eDescriptionLevelInitial, false); + +  return true; +} + +void RenderScriptRuntime::DumpModules(Stream &strm) const { +  strm.Printf("RenderScript Modules:"); +  strm.EOL(); +  strm.IndentMore(); +  for (const auto &module : m_rsmodules) { +    module->Dump(strm); +  } +  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) { +  for (const auto &a : m_allocations) { +    if (a->address.isValid()) +      if (*a->address == address) +        return a.get(); +  } +  return nullptr; +} + +RenderScriptRuntime::AllocationDetails * +RenderScriptRuntime::CreateAllocation(addr_t address) { +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); + +  // Remove any previous allocation which contains the same address +  auto it = m_allocations.begin(); +  while (it != m_allocations.end()) { +    if (*((*it)->address) == address) { +      if (log) +        log->Printf("%s - Removing allocation id: %d, address: 0x%" PRIx64, +                    __FUNCTION__, (*it)->id, address); + +      it = m_allocations.erase(it); +    } else { +      it++; +    } +  } + +  std::unique_ptr<AllocationDetails> a(new AllocationDetails); +  a->address = address; +  m_allocations.push_back(std::move(a)); +  return m_allocations.back().get(); +} + +bool RenderScriptRuntime::ResolveKernelName(lldb::addr_t kernel_addr, +                                            ConstString &name) { +  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); + +  Target &target = GetProcess()->GetTarget(); +  Address resolved; +  // RenderScript module +  if (!target.GetSectionLoadList().ResolveLoadAddress(kernel_addr, resolved)) { +    if (log) +      log->Printf("%s: unable to resolve 0x%" PRIx64 " to a loaded symbol", +                  __FUNCTION__, kernel_addr); +    return false; +  } + +  Symbol *sym = resolved.CalculateSymbolContextSymbol(); +  if (!sym) +    return false; + +  name = sym->GetName(); +  assert(IsRenderScriptModule(resolved.CalculateSymbolContextModule())); +  if (log) +    log->Printf("%s: 0x%" PRIx64 " resolved to the symbol '%s'", __FUNCTION__, +                kernel_addr, name.GetCString()); +  return true; +} + +void RSModuleDescriptor::Dump(Stream &strm) const { +  int indent = strm.GetIndentLevel(); + +  strm.Indent(); +  m_module->GetFileSpec().Dump(&strm); +  strm.Indent(m_module->GetNumCompileUnits() ? "Debug info loaded." +                                             : "Debug info does not exist."); +  strm.EOL(); +  strm.IndentMore(); + +  strm.Indent(); +  strm.Printf("Globals: %" PRIu64, static_cast<uint64_t>(m_globals.size())); +  strm.EOL(); +  strm.IndentMore(); +  for (const auto &global : m_globals) { +    global.Dump(strm); +  } +  strm.IndentLess(); + +  strm.Indent(); +  strm.Printf("Kernels: %" PRIu64, static_cast<uint64_t>(m_kernels.size())); +  strm.EOL(); +  strm.IndentMore(); +  for (const auto &kernel : m_kernels) { +    kernel.Dump(strm); +  } +  strm.IndentLess(); + +  strm.Indent(); +  strm.Printf("Pragmas: %" PRIu64, static_cast<uint64_t>(m_pragmas.size())); +  strm.EOL(); +  strm.IndentMore(); +  for (const auto &key_val : m_pragmas) { +    strm.Indent(); +    strm.Printf("%s: %s", key_val.first.c_str(), key_val.second.c_str()); +    strm.EOL(); +  } +  strm.IndentLess(); + +  strm.Indent(); +  strm.Printf("Reductions: %" PRIu64, +              static_cast<uint64_t>(m_reductions.size())); +  strm.EOL(); +  strm.IndentMore(); +  for (const auto &reduction : m_reductions) { +    reduction.Dump(strm); +  } + +  strm.SetIndentLevel(indent); +} + +void RSGlobalDescriptor::Dump(Stream &strm) const { +  strm.Indent(m_name.AsCString()); +  VariableList var_list; +  m_module->m_module->FindGlobalVariables(m_name, nullptr, 1U, var_list); +  if (var_list.GetSize() == 1) { +    auto var = var_list.GetVariableAtIndex(0); +    auto type = var->GetType(); +    if (type) { +      strm.Printf(" - "); +      type->DumpTypeName(&strm); +    } else { +      strm.Printf(" - Unknown Type"); +    } +  } else { +    strm.Printf(" - variable identified, but not found in binary"); +    const Symbol *s = m_module->m_module->FindFirstSymbolWithNameAndType( +        m_name, eSymbolTypeData); +    if (s) { +      strm.Printf(" (symbol exists) "); +    } +  } + +  strm.EOL(); +} + +void RSKernelDescriptor::Dump(Stream &strm) const { +  strm.Indent(m_name.AsCString()); +  strm.EOL(); +} + +void RSReductionDescriptor::Dump(lldb_private::Stream &stream) const { +  stream.Indent(m_reduce_name.AsCString()); +  stream.IndentMore(); +  stream.EOL(); +  stream.Indent(); +  stream.Printf("accumulator: %s", m_accum_name.AsCString()); +  stream.EOL(); +  stream.Indent(); +  stream.Printf("initializer: %s", m_init_name.AsCString()); +  stream.EOL(); +  stream.Indent(); +  stream.Printf("combiner: %s", m_comb_name.AsCString()); +  stream.EOL(); +  stream.Indent(); +  stream.Printf("outconverter: %s", m_outc_name.AsCString()); +  stream.EOL(); +  // XXX This is currently unspecified by RenderScript, and unused +  // stream.Indent(); +  // stream.Printf("halter: '%s'", m_init_name.AsCString()); +  // stream.EOL(); +  stream.IndentLess(); +} + +class CommandObjectRenderScriptRuntimeModuleDump : public CommandObjectParsed { +public: +  CommandObjectRenderScriptRuntimeModuleDump(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "renderscript module dump", +            "Dumps renderscript specific information for all modules.", +            "renderscript module dump", +            eCommandRequiresProcess | eCommandProcessMustBeLaunched) {} + +  ~CommandObjectRenderScriptRuntimeModuleDump() override = default; + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); +    runtime->DumpModules(result.GetOutputStream()); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } +}; + +class CommandObjectRenderScriptRuntimeModule : public CommandObjectMultiword { +public: +  CommandObjectRenderScriptRuntimeModule(CommandInterpreter &interpreter) +      : CommandObjectMultiword(interpreter, "renderscript module", +                               "Commands that deal with RenderScript modules.", +                               nullptr) { +    LoadSubCommand( +        "dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeModuleDump( +                    interpreter))); +  } + +  ~CommandObjectRenderScriptRuntimeModule() override = default; +}; + +class CommandObjectRenderScriptRuntimeKernelList : public CommandObjectParsed { +public: +  CommandObjectRenderScriptRuntimeKernelList(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "renderscript kernel list", +            "Lists renderscript kernel names and associated script resources.", +            "renderscript kernel list", +            eCommandRequiresProcess | eCommandProcessMustBeLaunched) {} + +  ~CommandObjectRenderScriptRuntimeKernelList() override = default; + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); +    runtime->DumpKernels(result.GetOutputStream()); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } +}; + +static constexpr OptionDefinition g_renderscript_reduction_bp_set_options[] = { +    {LLDB_OPT_SET_1, false, "function-role", 't', +     OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOneLiner, +     "Break on a comma separated set of reduction kernel types " +     "(accumulator,outcoverter,combiner,initializer"}, +    {LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument, +     nullptr, {}, 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."}}; + +class CommandObjectRenderScriptRuntimeReductionBreakpointSet +    : public CommandObjectParsed { +public: +  CommandObjectRenderScriptRuntimeReductionBreakpointSet( +      CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "renderscript reduction breakpoint set", +            "Set a breakpoint on named RenderScript general reductions", +            "renderscript reduction breakpoint set  <kernel_name> [-t " +            "<reduction_kernel_type,...>]", +            eCommandRequiresProcess | eCommandProcessMustBeLaunched | +                eCommandProcessMustBePaused), +        m_options(){}; + +  class CommandOptions : public Options { +  public: +    CommandOptions() +        : Options(), +          m_kernel_types(RSReduceBreakpointResolver::eKernelTypeAll) {} + +    ~CommandOptions() override = default; + +    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, +                          ExecutionContext *exe_ctx) override { +      Status err; +      StreamString err_str; +      const int short_option = m_getopt_table[option_idx].val; +      switch (short_option) { +      case 't': +        if (!ParseReductionTypes(option_arg, err_str)) +          err.SetErrorStringWithFormat( +              "Unable to deduce reduction types for %s: %s", +              option_arg.str().c_str(), err_str.GetData()); +        break; +      case 'c': { +        auto coord = RSCoordinate{}; +        if (!ParseCoordinate(option_arg, coord)) +          err.SetErrorStringWithFormat("unable to parse coordinate for %s", +                                       option_arg.str().c_str()); +        else { +          m_have_coord = true; +          m_coord = coord; +        } +        break; +      } +      default: +        err.SetErrorStringWithFormat("Invalid option '-%c'", short_option); +      } +      return err; +    } + +    void OptionParsingStarting(ExecutionContext *exe_ctx) override { +      m_have_coord = false; +    } + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_renderscript_reduction_bp_set_options); +    } + +    bool ParseReductionTypes(llvm::StringRef option_val, +                             StreamString &err_str) { +      m_kernel_types = RSReduceBreakpointResolver::eKernelTypeNone; +      const auto reduce_name_to_type = [](llvm::StringRef name) -> int { +        return llvm::StringSwitch<int>(name) +            .Case("accumulator", RSReduceBreakpointResolver::eKernelTypeAccum) +            .Case("initializer", RSReduceBreakpointResolver::eKernelTypeInit) +            .Case("outconverter", RSReduceBreakpointResolver::eKernelTypeOutC) +            .Case("combiner", RSReduceBreakpointResolver::eKernelTypeComb) +            .Case("all", RSReduceBreakpointResolver::eKernelTypeAll) +            // Currently not exposed by the runtime +            // .Case("halter", RSReduceBreakpointResolver::eKernelTypeHalter) +            .Default(0); +      }; + +      // Matching a comma separated list of known words is fairly +      // straightforward with PCRE, but we're using ERE, so we end up with a +      // little ugliness... +      RegularExpression::Match match(/* max_matches */ 5); +      RegularExpression match_type_list( +          llvm::StringRef("^([[:alpha:]]+)(,[[:alpha:]]+){0,4}$")); + +      assert(match_type_list.IsValid()); + +      if (!match_type_list.Execute(option_val, &match)) { +        err_str.PutCString( +            "a comma-separated list of kernel types is required"); +        return false; +      } + +      // splitting on commas is much easier with llvm::StringRef than regex +      llvm::SmallVector<llvm::StringRef, 5> type_names; +      llvm::StringRef(option_val).split(type_names, ','); + +      for (const auto &name : type_names) { +        const int type = reduce_name_to_type(name); +        if (!type) { +          err_str.Printf("unknown kernel type name %s", name.str().c_str()); +          return false; +        } +        m_kernel_types |= type; +      } + +      return true; +    } + +    int m_kernel_types; +    llvm::StringRef m_reduce_name; +    RSCoordinate m_coord; +    bool m_have_coord; +  }; + +  Options *GetOptions() override { return &m_options; } + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    const size_t argc = command.GetArgumentCount(); +    if (argc < 1) { +      result.AppendErrorWithFormat("'%s' takes 1 argument of reduction name, " +                                   "and an optional kernel type list", +                                   m_cmd_name.c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); + +    auto &outstream = result.GetOutputStream(); +    auto name = command.GetArgumentAtIndex(0); +    auto &target = m_exe_ctx.GetTargetSP(); +    auto coord = m_options.m_have_coord ? &m_options.m_coord : nullptr; +    if (!runtime->PlaceBreakpointOnReduction(target, outstream, name, coord, +                                             m_options.m_kernel_types)) { +      result.SetStatus(eReturnStatusFailed); +      result.AppendError("Error: unable to place breakpoint on reduction"); +      return false; +    } +    result.AppendMessage("Breakpoint(s) created"); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } + +private: +  CommandOptions m_options; +}; + +static constexpr OptionDefinition g_renderscript_kernel_bp_set_options[] = { +    {LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument, +     nullptr, {}, 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."}}; + +class CommandObjectRenderScriptRuntimeKernelBreakpointSet +    : public CommandObjectParsed { +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() {} + +  ~CommandObjectRenderScriptRuntimeKernelBreakpointSet() override = default; + +  Options *GetOptions() override { return &m_options; } + +  class CommandOptions : public Options { +  public: +    CommandOptions() : Options() {} + +    ~CommandOptions() override = default; + +    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, +                          ExecutionContext *exe_ctx) override { +      Status err; +      const int short_option = m_getopt_table[option_idx].val; + +      switch (short_option) { +      case 'c': { +        auto coord = RSCoordinate{}; +        if (!ParseCoordinate(option_arg, coord)) +          err.SetErrorStringWithFormat( +              "Couldn't parse coordinate '%s', should be in format 'x,y,z'.", +              option_arg.str().c_str()); +        else { +          m_have_coord = true; +          m_coord = coord; +        } +        break; +      } +      default: +        err.SetErrorStringWithFormat("unrecognized option '%c'", short_option); +        break; +      } +      return err; +    } + +    void OptionParsingStarting(ExecutionContext *exe_ctx) override { +      m_have_coord = false; +    } + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_renderscript_kernel_bp_set_options); +    } + +    RSCoordinate m_coord; +    bool m_have_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); +      return false; +    } + +    RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); + +    auto &outstream = result.GetOutputStream(); +    auto &target = m_exe_ctx.GetTargetSP(); +    auto name = command.GetArgumentAtIndex(0); +    auto coord = m_options.m_have_coord ? &m_options.m_coord : nullptr; +    if (!runtime->PlaceBreakpointOnKernel(target, outstream, name, coord)) { +      result.SetStatus(eReturnStatusFailed); +      result.AppendErrorWithFormat( +          "Error: unable to set breakpoint on kernel '%s'", name); +      return false; +    } + +    result.AppendMessage("Breakpoint(s) created"); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } + +private: +  CommandOptions m_options; +}; + +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 CommandObjectRenderScriptRuntimeReductionBreakpoint +    : public CommandObjectMultiword { +public: +  CommandObjectRenderScriptRuntimeReductionBreakpoint( +      CommandInterpreter &interpreter) +      : CommandObjectMultiword(interpreter, "renderscript reduction breakpoint", +                               "Commands that manipulate breakpoints on " +                               "renderscript general reductions.", +                               nullptr) { +    LoadSubCommand( +        "set", CommandObjectSP( +                   new CommandObjectRenderScriptRuntimeReductionBreakpointSet( +                       interpreter))); +  } + +  ~CommandObjectRenderScriptRuntimeReductionBreakpoint() override = default; +}; + +class CommandObjectRenderScriptRuntimeKernelCoordinate +    : public CommandObjectParsed { +public: +  CommandObjectRenderScriptRuntimeKernelCoordinate( +      CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "renderscript kernel coordinate", +            "Shows the (x,y,z) coordinate of the current kernel invocation.", +            "renderscript kernel coordinate", +            eCommandRequiresProcess | eCommandProcessMustBeLaunched | +                eCommandProcessMustBePaused) {} + +  ~CommandObjectRenderScriptRuntimeKernelCoordinate() override = default; + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    RSCoordinate coord{}; +    bool success = RenderScriptRuntime::GetKernelCoordinate( +        coord, m_exe_ctx.GetThreadPtr()); +    Stream &stream = result.GetOutputStream(); + +    if (success) { +      stream.Printf("Coordinate: " FMT_COORD, coord.x, coord.y, coord.z); +      stream.EOL(); +      result.SetStatus(eReturnStatusSuccessFinishResult); +    } else { +      stream.Printf("Error: Coordinate could not be found."); +      stream.EOL(); +      result.SetStatus(eReturnStatusFailed); +    } +    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 { +public: +  CommandObjectRenderScriptRuntimeKernel(CommandInterpreter &interpreter) +      : CommandObjectMultiword(interpreter, "renderscript kernel", +                               "Commands that deal with RenderScript kernels.", +                               nullptr) { +    LoadSubCommand( +        "list", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelList( +                    interpreter))); +    LoadSubCommand( +        "coordinate", +        CommandObjectSP( +            new CommandObjectRenderScriptRuntimeKernelCoordinate(interpreter))); +    LoadSubCommand( +        "breakpoint", +        CommandObjectSP( +            new CommandObjectRenderScriptRuntimeKernelBreakpoint(interpreter))); +  } + +  ~CommandObjectRenderScriptRuntimeKernel() override = default; +}; + +class CommandObjectRenderScriptRuntimeContextDump : public CommandObjectParsed { +public: +  CommandObjectRenderScriptRuntimeContextDump(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "renderscript context dump", +                            "Dumps renderscript context information.", +                            "renderscript context dump", +                            eCommandRequiresProcess | +                                eCommandProcessMustBeLaunched) {} + +  ~CommandObjectRenderScriptRuntimeContextDump() override = default; + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); +    runtime->DumpContexts(result.GetOutputStream()); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } +}; + +static constexpr OptionDefinition g_renderscript_runtime_alloc_dump_options[] = { +    {LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, +     nullptr, {}, 0, eArgTypeFilename, +     "Print results to specified file instead of command line."}}; + +class CommandObjectRenderScriptRuntimeContext : public CommandObjectMultiword { +public: +  CommandObjectRenderScriptRuntimeContext(CommandInterpreter &interpreter) +      : CommandObjectMultiword(interpreter, "renderscript context", +                               "Commands that deal with RenderScript contexts.", +                               nullptr) { +    LoadSubCommand( +        "dump", CommandObjectSP(new CommandObjectRenderScriptRuntimeContextDump( +                    interpreter))); +  } + +  ~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() {} + +  ~CommandObjectRenderScriptRuntimeAllocationDump() override = default; + +  Options *GetOptions() override { return &m_options; } + +  class CommandOptions : public Options { +  public: +    CommandOptions() : Options() {} + +    ~CommandOptions() override = default; + +    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, +                          ExecutionContext *exe_ctx) override { +      Status err; +      const int short_option = m_getopt_table[option_idx].val; + +      switch (short_option) { +      case 'f': +        m_outfile.SetFile(option_arg, FileSpec::Style::native); +        FileSystem::Instance().Resolve(m_outfile); +        if (FileSystem::Instance().Exists(m_outfile)) { +          m_outfile.Clear(); +          err.SetErrorStringWithFormat("file already exists: '%s'", +                                       option_arg.str().c_str()); +        } +        break; +      default: +        err.SetErrorStringWithFormat("unrecognized option '%c'", short_option); +        break; +      } +      return err; +    } + +    void OptionParsingStarting(ExecutionContext *exe_ctx) override { +      m_outfile.Clear(); +    } + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_renderscript_runtime_alloc_dump_options); +    } + +    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 success = false; +    const uint32_t id = +        StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success); +    if (!success) { +      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 +      std::string path = outfile_spec.GetPath(); +      auto error = FileSystem::Instance().Open( +          outfile_stream.GetFile(), outfile_spec, +          File::eOpenOptionWrite | File::eOpenOptionCanCreate); +      if (error.Success()) { +        output_strm = &outfile_stream; +        result.GetOutputStream().Printf("Results written to '%s'", +                                        path.c_str()); +        result.GetOutputStream().EOL(); +      } else { +        result.AppendErrorWithFormat("Couldn't open file '%s'", path.c_str()); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +    } else +      output_strm = &result.GetOutputStream(); + +    assert(output_strm != nullptr); +    bool dumped = +        runtime->DumpAllocation(*output_strm, m_exe_ctx.GetFramePtr(), id); + +    if (dumped) +      result.SetStatus(eReturnStatusSuccessFinishResult); +    else +      result.SetStatus(eReturnStatusFailed); + +    return true; +  } + +private: +  CommandOptions m_options; +}; + +static constexpr OptionDefinition g_renderscript_runtime_alloc_list_options[] = { +    {LLDB_OPT_SET_1, false, "id", 'i', OptionParser::eRequiredArgument, nullptr, +     {}, 0, eArgTypeIndex, +     "Only show details of a single allocation with specified id."}}; + +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() {} + +  ~CommandObjectRenderScriptRuntimeAllocationList() override = default; + +  Options *GetOptions() override { return &m_options; } + +  class CommandOptions : public Options { +  public: +    CommandOptions() : Options(), m_id(0) {} + +    ~CommandOptions() override = default; + +    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, +                          ExecutionContext *exe_ctx) override { +      Status err; +      const int short_option = m_getopt_table[option_idx].val; + +      switch (short_option) { +      case 'i': +        if (option_arg.getAsInteger(0, m_id)) +          err.SetErrorStringWithFormat("invalid integer value for option '%c'", +                                       short_option); +        break; +      default: +        err.SetErrorStringWithFormat("unrecognized option '%c'", short_option); +        break; +      } +      return err; +    } + +    void OptionParsingStarting(ExecutionContext *exe_ctx) override { m_id = 0; } + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_renderscript_runtime_alloc_list_options); +    } + +    uint32_t m_id; +  }; + +  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_id); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } + +private: +  CommandOptions m_options; +}; + +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 success = false; +    const uint32_t id = +        StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success); +    if (!success) { +      result.AppendErrorWithFormat("invalid allocation id argument '%s'", +                                   id_cstr); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    const char *path = command.GetArgumentAtIndex(1); +    bool loaded = runtime->LoadAllocation(result.GetOutputStream(), id, path, +                                          m_exe_ctx.GetFramePtr()); + +    if (loaded) +      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 success = false; +    const uint32_t id = +        StringConvert::ToUInt32(id_cstr, UINT32_MAX, 0, &success); +    if (!success) { +      result.AppendErrorWithFormat("invalid allocation id argument '%s'", +                                   id_cstr); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    const char *path = command.GetArgumentAtIndex(1); +    bool saved = runtime->SaveAllocation(result.GetOutputStream(), id, path, +                                         m_exe_ctx.GetFramePtr()); + +    if (saved) +      result.SetStatus(eReturnStatusSuccessFinishResult); +    else +      result.SetStatus(eReturnStatusFailed); + +    return true; +  } +}; + +class CommandObjectRenderScriptRuntimeAllocationRefresh +    : public CommandObjectParsed { +public: +  CommandObjectRenderScriptRuntimeAllocationRefresh( +      CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "renderscript allocation refresh", +                            "Recomputes the details of all allocations.", +                            "renderscript allocation refresh", +                            eCommandRequiresProcess | +                                eCommandProcessMustBeLaunched) {} + +  ~CommandObjectRenderScriptRuntimeAllocationRefresh() override = default; + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); + +    bool success = runtime->RecomputeAllAllocations(result.GetOutputStream(), +                                                    m_exe_ctx.GetFramePtr()); + +    if (success) { +      result.SetStatus(eReturnStatusSuccessFinishResult); +      return true; +    } else { +      result.SetStatus(eReturnStatusFailed); +      return false; +    } +  } +}; + +class CommandObjectRenderScriptRuntimeAllocation +    : public CommandObjectMultiword { +public: +  CommandObjectRenderScriptRuntimeAllocation(CommandInterpreter &interpreter) +      : CommandObjectMultiword( +            interpreter, "renderscript allocation", +            "Commands that deal with RenderScript allocations.", nullptr) { +    LoadSubCommand( +        "list", +        CommandObjectSP( +            new CommandObjectRenderScriptRuntimeAllocationList(interpreter))); +    LoadSubCommand( +        "dump", +        CommandObjectSP( +            new CommandObjectRenderScriptRuntimeAllocationDump(interpreter))); +    LoadSubCommand( +        "save", +        CommandObjectSP( +            new CommandObjectRenderScriptRuntimeAllocationSave(interpreter))); +    LoadSubCommand( +        "load", +        CommandObjectSP( +            new CommandObjectRenderScriptRuntimeAllocationLoad(interpreter))); +    LoadSubCommand( +        "refresh", +        CommandObjectSP(new CommandObjectRenderScriptRuntimeAllocationRefresh( +            interpreter))); +  } + +  ~CommandObjectRenderScriptRuntimeAllocation() override = default; +}; + +class CommandObjectRenderScriptRuntimeStatus : public CommandObjectParsed { +public: +  CommandObjectRenderScriptRuntimeStatus(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "renderscript status", +                            "Displays current RenderScript runtime status.", +                            "renderscript status", +                            eCommandRequiresProcess | +                                eCommandProcessMustBeLaunched) {} + +  ~CommandObjectRenderScriptRuntimeStatus() override = default; + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    RenderScriptRuntime *runtime = llvm::cast<RenderScriptRuntime>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); +    runtime->DumpStatus(result.GetOutputStream()); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } +}; + +class CommandObjectRenderScriptRuntimeReduction +    : public CommandObjectMultiword { +public: +  CommandObjectRenderScriptRuntimeReduction(CommandInterpreter &interpreter) +      : CommandObjectMultiword(interpreter, "renderscript reduction", +                               "Commands that handle general reduction kernels", +                               nullptr) { +    LoadSubCommand( +        "breakpoint", +        CommandObjectSP(new CommandObjectRenderScriptRuntimeReductionBreakpoint( +            interpreter))); +  } +  ~CommandObjectRenderScriptRuntimeReduction() override = default; +}; + +class CommandObjectRenderScriptRuntime : public CommandObjectMultiword { +public: +  CommandObjectRenderScriptRuntime(CommandInterpreter &interpreter) +      : CommandObjectMultiword( +            interpreter, "renderscript", +            "Commands for operating on the RenderScript runtime.", +            "renderscript <subcommand> [<subcommand-options>]") { +    LoadSubCommand( +        "module", CommandObjectSP( +                      new CommandObjectRenderScriptRuntimeModule(interpreter))); +    LoadSubCommand( +        "status", CommandObjectSP( +                      new CommandObjectRenderScriptRuntimeStatus(interpreter))); +    LoadSubCommand( +        "kernel", CommandObjectSP( +                      new CommandObjectRenderScriptRuntimeKernel(interpreter))); +    LoadSubCommand("context", +                   CommandObjectSP(new CommandObjectRenderScriptRuntimeContext( +                       interpreter))); +    LoadSubCommand( +        "allocation", +        CommandObjectSP( +            new CommandObjectRenderScriptRuntimeAllocation(interpreter))); +    LoadSubCommand("scriptgroup", +                   NewCommandObjectRenderScriptScriptGroup(interpreter)); +    LoadSubCommand( +        "reduction", +        CommandObjectSP( +            new CommandObjectRenderScriptRuntimeReduction(interpreter))); +  } + +  ~CommandObjectRenderScriptRuntime() override = default; +}; + +void RenderScriptRuntime::Initiate() { assert(!m_initiated); } + +RenderScriptRuntime::RenderScriptRuntime(Process *process) +    : lldb_private::CPPLanguageRuntime(process), m_initiated(false), +      m_debuggerPresentFlagged(false), m_breakAllKernels(false), +      m_ir_passes(nullptr) { +  ModulesDidLoad(process->GetTarget().GetImages()); +} + +lldb::CommandObjectSP RenderScriptRuntime::GetCommandObject( +    lldb_private::CommandInterpreter &interpreter) { +  return CommandObjectSP(new CommandObjectRenderScriptRuntime(interpreter)); +} + +RenderScriptRuntime::~RenderScriptRuntime() = default; diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h new file mode 100644 index 000000000000..3923221d4302 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h @@ -0,0 +1,587 @@ +//===-- RenderScriptRuntime.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RenderScriptRuntime_h_ +#define liblldb_RenderScriptRuntime_h_ + +#include <array> +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "lldb/Core/Module.h" +#include "lldb/Expression/LLVMUserExpression.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/lldb-private.h" + +#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" + +namespace lldb_private { +namespace lldb_renderscript { + +typedef uint32_t RSSlot; +class RSModuleDescriptor; +struct RSGlobalDescriptor; +struct RSKernelDescriptor; +struct RSReductionDescriptor; +struct RSScriptGroupDescriptor; + +typedef std::shared_ptr<RSModuleDescriptor> RSModuleDescriptorSP; +typedef std::shared_ptr<RSGlobalDescriptor> RSGlobalDescriptorSP; +typedef std::shared_ptr<RSKernelDescriptor> RSKernelDescriptorSP; +typedef std::shared_ptr<RSScriptGroupDescriptor> RSScriptGroupDescriptorSP; + +struct RSCoordinate { +  uint32_t x, y, z; + +  RSCoordinate() : x(), y(), z(){}; + +  bool operator==(const lldb_renderscript::RSCoordinate &rhs) { +    return x == rhs.x && y == rhs.y && z == rhs.z; +  } +}; + +// Breakpoint Resolvers decide where a breakpoint is placed, so having our own +// allows us to limit the search scope to RS kernel modules. As well as check +// for .expand kernels as a fallback. +class RSBreakpointResolver : public BreakpointResolver { +public: +  RSBreakpointResolver(Breakpoint *bp, ConstString name) +      : BreakpointResolver(bp, BreakpointResolver::NameResolver), +        m_kernel_name(name) {} + +  void GetDescription(Stream *strm) override { +    if (strm) +      strm->Printf("RenderScript kernel breakpoint for '%s'", +                   m_kernel_name.AsCString()); +  } + +  void Dump(Stream *s) const override {} + +  Searcher::CallbackReturn SearchCallback(SearchFilter &filter, +                                          SymbolContext &context, Address *addr, +                                          bool containing) override; + +  lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } + +  lldb::BreakpointResolverSP +  CopyForBreakpoint(Breakpoint &breakpoint) override { +    lldb::BreakpointResolverSP ret_sp( +        new RSBreakpointResolver(&breakpoint, m_kernel_name)); +    return ret_sp; +  } + +protected: +  ConstString m_kernel_name; +}; + +class RSReduceBreakpointResolver : public BreakpointResolver { +public: +  enum ReduceKernelTypeFlags { +    eKernelTypeAll = ~(0), +    eKernelTypeNone = 0, +    eKernelTypeAccum = (1 << 0), +    eKernelTypeInit = (1 << 1), +    eKernelTypeComb = (1 << 2), +    eKernelTypeOutC = (1 << 3), +    eKernelTypeHalter = (1 << 4) +  }; + +  RSReduceBreakpointResolver( +      Breakpoint *breakpoint, ConstString reduce_name, +      std::vector<lldb_renderscript::RSModuleDescriptorSP> *rs_modules, +      int kernel_types = eKernelTypeAll) +      : BreakpointResolver(breakpoint, BreakpointResolver::NameResolver), +        m_reduce_name(reduce_name), m_rsmodules(rs_modules), +        m_kernel_types(kernel_types) { +    // The reduce breakpoint resolver handles adding breakpoints for named +    // reductions. +    // Breakpoints will be resolved for all constituent kernels in the named +    // reduction +  } + +  void GetDescription(Stream *strm) override { +    if (strm) +      strm->Printf("RenderScript reduce breakpoint for '%s'", +                   m_reduce_name.AsCString()); +  } + +  void Dump(Stream *s) const override {} + +  Searcher::CallbackReturn SearchCallback(SearchFilter &filter, +                                          SymbolContext &context, Address *addr, +                                          bool containing) override; + +  lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } + +  lldb::BreakpointResolverSP +  CopyForBreakpoint(Breakpoint &breakpoint) override { +    lldb::BreakpointResolverSP ret_sp(new RSReduceBreakpointResolver( +        &breakpoint, m_reduce_name, m_rsmodules, m_kernel_types)); +    return ret_sp; +  } + +private: +  ConstString m_reduce_name; // The name of the reduction +  std::vector<lldb_renderscript::RSModuleDescriptorSP> *m_rsmodules; +  int m_kernel_types; +}; + +struct RSKernelDescriptor { +public: +  RSKernelDescriptor(const RSModuleDescriptor *module, llvm::StringRef name, +                     uint32_t slot) +      : m_module(module), m_name(name), m_slot(slot) {} + +  void Dump(Stream &strm) const; + +  const RSModuleDescriptor *m_module; +  ConstString m_name; +  RSSlot m_slot; +}; + +struct RSGlobalDescriptor { +public: +  RSGlobalDescriptor(const RSModuleDescriptor *module, llvm::StringRef name) +      : m_module(module), m_name(name) {} + +  void Dump(Stream &strm) const; + +  const RSModuleDescriptor *m_module; +  ConstString m_name; +}; + +struct RSReductionDescriptor { +  RSReductionDescriptor(const RSModuleDescriptor *module, uint32_t sig, +                        uint32_t accum_data_size, llvm::StringRef name, +                        llvm::StringRef init_name, llvm::StringRef accum_name, +                        llvm::StringRef comb_name, llvm::StringRef outc_name, +                        llvm::StringRef halter_name = ".") +      : m_module(module), m_reduce_name(name), m_init_name(init_name), +        m_accum_name(accum_name), m_comb_name(comb_name), +        m_outc_name(outc_name), m_halter_name(halter_name) { +    // TODO Check whether the combiner is an autogenerated name, and track +    // this +  } + +  void Dump(Stream &strm) const; + +  const RSModuleDescriptor *m_module; +  ConstString m_reduce_name; // This is the name given to the general reduction +                             // as a group as passed to pragma +  // reduce(m_reduce_name). There is no kernel function with this name +  ConstString m_init_name;  // The name of the initializer name. "." if no +                            // initializer given +  ConstString m_accum_name; // The accumulator function name. "." if not given +  ConstString m_comb_name; // The name of the combiner function. If this was not +                           // given, a name is generated by the +                           // compiler. TODO +  ConstString m_outc_name; // The name of the outconverter + +  ConstString m_halter_name; // The name of the halter function. XXX This is not +                             // yet specified by the RenderScript +  // compiler or runtime, and its semantics and existence is still under +  // discussion by the +  // RenderScript Contributors +  RSSlot m_accum_sig; // metatdata signature for this reduction (bitwise mask of +                      // type information (see +                      // libbcc/include/bcinfo/MetadataExtractor.h +  uint32_t m_accum_data_size; // Data size of the accumulator function input +  bool m_comb_name_generated; // Was the combiner name generated by the compiler +}; + +class RSModuleDescriptor { +  std::string m_slang_version; +  std::string m_bcc_version; + +  bool ParseVersionInfo(llvm::StringRef *, size_t n_lines); + +  bool ParseExportForeachCount(llvm::StringRef *, size_t n_lines); + +  bool ParseExportVarCount(llvm::StringRef *, size_t n_lines); + +  bool ParseExportReduceCount(llvm::StringRef *, size_t n_lines); + +  bool ParseBuildChecksum(llvm::StringRef *, size_t n_lines); + +  bool ParsePragmaCount(llvm::StringRef *, size_t n_lines); + +public: +  RSModuleDescriptor(const lldb::ModuleSP &module) : m_module(module) {} + +  ~RSModuleDescriptor() = default; + +  bool ParseRSInfo(); + +  void Dump(Stream &strm) const; + +  void WarnIfVersionMismatch(Stream *s) const; + +  const lldb::ModuleSP m_module; +  std::vector<RSKernelDescriptor> m_kernels; +  std::vector<RSGlobalDescriptor> m_globals; +  std::vector<RSReductionDescriptor> m_reductions; +  std::map<std::string, std::string> m_pragmas; +  std::string m_resname; +}; + +struct RSScriptGroupDescriptor { +  struct Kernel { +    ConstString m_name; +    lldb::addr_t m_addr; +  }; +  ConstString m_name; +  std::vector<Kernel> m_kernels; +}; + +typedef std::vector<RSScriptGroupDescriptorSP> RSScriptGroupList; + +class RSScriptGroupBreakpointResolver : public BreakpointResolver { +public: +  RSScriptGroupBreakpointResolver(Breakpoint *bp, ConstString name, +                                  const RSScriptGroupList &groups, +                                  bool stop_on_all) +      : BreakpointResolver(bp, BreakpointResolver::NameResolver), +        m_group_name(name), m_script_groups(groups), +        m_stop_on_all(stop_on_all) {} + +  void GetDescription(Stream *strm) override { +    if (strm) +      strm->Printf("RenderScript ScriptGroup breakpoint for '%s'", +                   m_group_name.AsCString()); +  } + +  void Dump(Stream *s) const override {} + +  Searcher::CallbackReturn SearchCallback(SearchFilter &filter, +                                          SymbolContext &context, Address *addr, +                                          bool containing) override; + +  lldb::SearchDepth GetDepth() override { return lldb::eSearchDepthModule; } + +  lldb::BreakpointResolverSP +  CopyForBreakpoint(Breakpoint &breakpoint) override { +    lldb::BreakpointResolverSP ret_sp(new RSScriptGroupBreakpointResolver( +        &breakpoint, m_group_name, m_script_groups, m_stop_on_all)); +    return ret_sp; +  } + +protected: +  const RSScriptGroupDescriptorSP +  FindScriptGroup(ConstString name) const { +    for (auto sg : m_script_groups) { +      if (ConstString::Compare(sg->m_name, name) == 0) +        return sg; +    } +    return RSScriptGroupDescriptorSP(); +  } + +  ConstString m_group_name; +  const RSScriptGroupList &m_script_groups; +  bool m_stop_on_all; +}; +} // namespace lldb_renderscript + +class RenderScriptRuntime : public lldb_private::CPPLanguageRuntime { +public: +  enum ModuleKind { +    eModuleKindIgnored, +    eModuleKindLibRS, +    eModuleKindDriver, +    eModuleKindImpl, +    eModuleKindKernelObj +  }; + +  ~RenderScriptRuntime() override; + +  // Static Functions +  static void Initialize(); + +  static void Terminate(); + +  static lldb_private::LanguageRuntime * +  CreateInstance(Process *process, lldb::LanguageType language); + +  static lldb::CommandObjectSP +  GetCommandObject(CommandInterpreter &interpreter); + +  static lldb_private::ConstString GetPluginNameStatic(); + +  static char ID; + +  bool isA(const void *ClassID) const override { +    return ClassID == &ID || CPPLanguageRuntime::isA(ClassID); +  } + +  static bool classof(const LanguageRuntime *runtime) { +    return runtime->isA(&ID); +  } + +  static bool IsRenderScriptModule(const lldb::ModuleSP &module_sp); + +  static ModuleKind GetModuleKind(const lldb::ModuleSP &module_sp); + +  static void ModulesDidLoad(const lldb::ProcessSP &process_sp, +                             const ModuleList &module_list); + +  bool GetDynamicTypeAndAddress(ValueObject &in_value, +                                lldb::DynamicValueType use_dynamic, +                                TypeAndOrName &class_type_or_name, +                                Address &address, +                                Value::ValueType &value_type) override; + +  TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name, +                                 ValueObject &static_value) override; + +  bool CouldHaveDynamicValue(ValueObject &in_value) override; + +  lldb::BreakpointResolverSP CreateExceptionResolver(Breakpoint *bp, +                                                     bool catch_bp, +                                                     bool throw_bp) override; + +  bool LoadModule(const lldb::ModuleSP &module_sp); + +  void DumpModules(Stream &strm) const; + +  void DumpContexts(Stream &strm) const; + +  void DumpKernels(Stream &strm) const; + +  bool DumpAllocation(Stream &strm, StackFrame *frame_ptr, const uint32_t id); + +  void ListAllocations(Stream &strm, StackFrame *frame_ptr, +                       const uint32_t index); + +  bool RecomputeAllAllocations(Stream &strm, StackFrame *frame_ptr); + +  bool PlaceBreakpointOnKernel( +      lldb::TargetSP target, Stream &messages, const char *name, +      const lldb_renderscript::RSCoordinate *coords = nullptr); + +  bool PlaceBreakpointOnReduction( +      lldb::TargetSP target, Stream &messages, const char *reduce_name, +      const lldb_renderscript::RSCoordinate *coords = nullptr, +      int kernel_types = ~(0)); + +  bool PlaceBreakpointOnScriptGroup(lldb::TargetSP target, Stream &strm, +                                    ConstString name, bool stop_on_all); + +  void SetBreakAllKernels(bool do_break, lldb::TargetSP target); + +  void DumpStatus(Stream &strm) const; + +  void ModulesDidLoad(const ModuleList &module_list) override; + +  bool LoadAllocation(Stream &strm, const uint32_t alloc_id, +                      const char *filename, StackFrame *frame_ptr); + +  bool SaveAllocation(Stream &strm, const uint32_t alloc_id, +                      const char *filename, StackFrame *frame_ptr); + +  void Update(); + +  void Initiate(); + +  const lldb_renderscript::RSScriptGroupList &GetScriptGroups() const { +    return m_scriptGroups; +  }; + +  bool IsKnownKernel(ConstString name) { +    for (const auto &module : m_rsmodules) +      for (const auto &kernel : module->m_kernels) +        if (kernel.m_name == name) +          return true; +    return false; +  } + +  // PluginInterface protocol +  lldb_private::ConstString GetPluginName() override; + +  uint32_t GetPluginVersion() override; + +  static bool GetKernelCoordinate(lldb_renderscript::RSCoordinate &coord, +                                  Thread *thread_ptr); + +  bool ResolveKernelName(lldb::addr_t kernel_address, ConstString &name); + +protected: +  struct ScriptDetails; +  struct AllocationDetails; +  struct Element; + +  lldb_renderscript::RSScriptGroupList m_scriptGroups; + +  void InitSearchFilter(lldb::TargetSP target) { +    if (!m_filtersp) +      m_filtersp.reset(new SearchFilterForUnconstrainedSearches(target)); +  } + +  void FixupScriptDetails(lldb_renderscript::RSModuleDescriptorSP rsmodule_sp); + +  void LoadRuntimeHooks(lldb::ModuleSP module, ModuleKind kind); + +  bool RefreshAllocation(AllocationDetails *alloc, StackFrame *frame_ptr); + +  bool EvalRSExpression(const char *expression, StackFrame *frame_ptr, +                        uint64_t *result); + +  lldb::BreakpointSP CreateScriptGroupBreakpoint(ConstString name, +                                                 bool multi); + +  lldb::BreakpointSP CreateKernelBreakpoint(ConstString name); + +  lldb::BreakpointSP CreateReductionBreakpoint(ConstString name, +                                               int kernel_types); + +  void BreakOnModuleKernels( +      const lldb_renderscript::RSModuleDescriptorSP rsmodule_sp); + +  struct RuntimeHook; +  typedef void (RenderScriptRuntime::*CaptureStateFn)( +      RuntimeHook *hook_info, +      ExecutionContext &context); // Please do this! + +  struct HookDefn { +    const char *name; +    const char *symbol_name_m32; // mangled name for the 32 bit architectures +    const char *symbol_name_m64; // mangled name for the 64 bit archs +    uint32_t version; +    ModuleKind kind; +    CaptureStateFn grabber; +  }; + +  struct RuntimeHook { +    lldb::addr_t address; +    const HookDefn *defn; +    lldb::BreakpointSP bp_sp; +  }; + +  typedef std::shared_ptr<RuntimeHook> RuntimeHookSP; + +  lldb::ModuleSP m_libRS; +  lldb::ModuleSP m_libRSDriver; +  lldb::ModuleSP m_libRSCpuRef; +  std::vector<lldb_renderscript::RSModuleDescriptorSP> m_rsmodules; + +  std::vector<std::unique_ptr<ScriptDetails>> m_scripts; +  std::vector<std::unique_ptr<AllocationDetails>> m_allocations; + +  std::map<lldb::addr_t, lldb_renderscript::RSModuleDescriptorSP> +      m_scriptMappings; +  std::map<lldb::addr_t, RuntimeHookSP> m_runtimeHooks; +  std::map<lldb::user_id_t, std::unique_ptr<lldb_renderscript::RSCoordinate>> +      m_conditional_breaks; + +  lldb::SearchFilterSP +      m_filtersp; // Needed to create breakpoints through Target API + +  bool m_initiated; +  bool m_debuggerPresentFlagged; +  bool m_breakAllKernels; +  static const HookDefn s_runtimeHookDefns[]; +  static const size_t s_runtimeHookCount; +  LLVMUserExpression::IRPasses *m_ir_passes; + +private: +  RenderScriptRuntime(Process *process); // Call CreateInstance instead. + +  static bool HookCallback(void *baton, StoppointCallbackContext *ctx, +                           lldb::user_id_t break_id, +                           lldb::user_id_t break_loc_id); + +  static bool KernelBreakpointHit(void *baton, StoppointCallbackContext *ctx, +                                  lldb::user_id_t break_id, +                                  lldb::user_id_t break_loc_id); + +  void HookCallback(RuntimeHook *hook_info, ExecutionContext &context); + +  // Callback function when 'debugHintScriptGroup2' executes on the target. +  void CaptureDebugHintScriptGroup2(RuntimeHook *hook_info, +                                    ExecutionContext &context); + +  void CaptureScriptInit(RuntimeHook *hook_info, ExecutionContext &context); + +  void CaptureAllocationInit(RuntimeHook *hook_info, ExecutionContext &context); + +  void CaptureAllocationDestroy(RuntimeHook *hook_info, +                                ExecutionContext &context); + +  void CaptureSetGlobalVar(RuntimeHook *hook_info, ExecutionContext &context); + +  void CaptureScriptInvokeForEachMulti(RuntimeHook *hook_info, +                                       ExecutionContext &context); + +  AllocationDetails *FindAllocByID(Stream &strm, const uint32_t alloc_id); + +  std::shared_ptr<uint8_t> GetAllocationData(AllocationDetails *alloc, +                                             StackFrame *frame_ptr); + +  void SetElementSize(Element &elem); + +  static bool GetFrameVarAsUnsigned(const lldb::StackFrameSP, +                                    const char *var_name, uint64_t &val); + +  void FindStructTypeName(Element &elem, StackFrame *frame_ptr); + +  size_t PopulateElementHeaders(const std::shared_ptr<uint8_t> header_buffer, +                                size_t offset, const Element &elem); + +  size_t CalculateElementHeaderSize(const Element &elem); + +  void SetConditional(lldb::BreakpointSP bp, lldb_private::Stream &messages, +                      const lldb_renderscript::RSCoordinate &coord); +  // +  // Helper functions for jitting the runtime +  // + +  bool JITDataPointer(AllocationDetails *alloc, StackFrame *frame_ptr, +                      uint32_t x = 0, uint32_t y = 0, uint32_t z = 0); + +  bool JITTypePointer(AllocationDetails *alloc, StackFrame *frame_ptr); + +  bool JITTypePacked(AllocationDetails *alloc, StackFrame *frame_ptr); + +  bool JITElementPacked(Element &elem, const lldb::addr_t context, +                        StackFrame *frame_ptr); + +  bool JITAllocationSize(AllocationDetails *alloc, StackFrame *frame_ptr); + +  bool JITSubelements(Element &elem, const lldb::addr_t context, +                      StackFrame *frame_ptr); + +  bool JITAllocationStride(AllocationDetails *alloc, StackFrame *frame_ptr); + +  // Search for a script detail object using a target address. +  // If a script does not currently exist this function will return nullptr. +  // If 'create' is true and there is no previous script with this address, +  // then a new Script detail object will be created for this address and +  // returned. +  ScriptDetails *LookUpScript(lldb::addr_t address, bool create); + +  // Search for a previously saved allocation detail object using a target +  // address. +  // If an allocation does not exist for this address then nullptr will be +  // returned. +  AllocationDetails *LookUpAllocation(lldb::addr_t address); + +  // Creates a new allocation with the specified address assigning a new ID and +  // removes +  // any previous stored allocation which has the same address. +  AllocationDetails *CreateAllocation(lldb::addr_t address); + +  bool GetOverrideExprOptions(clang::TargetOptions &prototype) override; + +  bool GetIRPasses(LLVMUserExpression::IRPasses &passes) override; +}; + +} // namespace lldb_private + +#endif // liblldb_RenderScriptRuntime_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp new file mode 100644 index 000000000000..45d0d028d047 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.cpp @@ -0,0 +1,160 @@ +//===-- RenderScriptScriptGroup.cpp -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" + +#include "RenderScriptRuntime.h" +#include "RenderScriptScriptGroup.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_renderscript; + +class CommandObjectRenderScriptScriptGroupBreakpointSet +    : public CommandObjectParsed { +public: +  CommandObjectRenderScriptScriptGroupBreakpointSet( +      CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "renderscript scriptgroup breakpoint set", +            "Place a breakpoint on all kernels forming a script group.", +            "renderscript scriptgroup breakpoint set <group_name>", +            eCommandRequiresProcess | eCommandProcessMustBeLaunched) {} + +  ~CommandObjectRenderScriptScriptGroupBreakpointSet() override = default; + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    Stream &stream = result.GetOutputStream(); +    RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); +    assert(runtime); +    auto &target = m_exe_ctx.GetTargetSP(); +    bool stop_on_all = false; +    const llvm::StringRef long_stop_all("--stop-on-all"), short_stop_all("-a"); +    std::vector<ConstString> sites; +    sites.reserve(command.GetArgumentCount()); +    for (size_t i = 0; i < command.GetArgumentCount(); ++i) { +      const auto arg = command.GetArgumentAtIndex(i); +      if (long_stop_all == arg || short_stop_all == arg) +        stop_on_all = true; +      else +        sites.push_back(ConstString(arg)); +    } +    for (const auto &name : sites) { +      runtime->PlaceBreakpointOnScriptGroup(target, stream, name, stop_on_all); +    } +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } +}; + +class CommandObjectRenderScriptScriptGroupBreakpoint +    : public CommandObjectMultiword { +public: +  CommandObjectRenderScriptScriptGroupBreakpoint( +      CommandInterpreter &interpreter) +      : CommandObjectMultiword( +            interpreter, "renderscript scriptgroup breakpoint", +            "Renderscript scriptgroup breakpoint interaction.", +            "renderscript scriptgroup breakpoint set [--stop-on-all/-a]" +            "<scriptgroup name> ...", +            eCommandRequiresProcess | eCommandProcessMustBeLaunched) { +    LoadSubCommand( +        "set", +        CommandObjectSP(new CommandObjectRenderScriptScriptGroupBreakpointSet( +            interpreter))); +  } + +  ~CommandObjectRenderScriptScriptGroupBreakpoint() override = default; +}; + +class CommandObjectRenderScriptScriptGroupList : public CommandObjectParsed { +public: +  CommandObjectRenderScriptScriptGroupList(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "renderscript scriptgroup list", +                            "List all currently discovered script groups.", +                            "renderscript scriptgroup list", +                            eCommandRequiresProcess | +                                eCommandProcessMustBeLaunched) {} + +  ~CommandObjectRenderScriptScriptGroupList() override = default; + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    Stream &stream = result.GetOutputStream(); +    RenderScriptRuntime *runtime = static_cast<RenderScriptRuntime *>( +        m_exe_ctx.GetProcessPtr()->GetLanguageRuntime( +            eLanguageTypeExtRenderScript)); +    assert(runtime); +    const RSScriptGroupList &groups = runtime->GetScriptGroups(); +    // print script group count +    stream.Printf("%" PRIu64 " script %s", uint64_t(groups.size()), +                  (groups.size() == 1) ? "group" : "groups"); +    stream.EOL(); +    // print script group details +    stream.IndentMore(); +    for (const RSScriptGroupDescriptorSP &g : groups) { +      if (g) { +        stream.Indent(); +        // script group name +        stream.Printf("%s", g->m_name.AsCString()); +        stream.EOL(); +        // print out the kernels +        stream.IndentMore(); +        for (const auto &k : g->m_kernels) { +          stream.Indent(); +          stream.Printf(". %s", k.m_name.AsCString()); +          stream.EOL(); +        } +        stream.IndentLess(); +      } +    } +    stream.IndentLess(); +    result.SetStatus(eReturnStatusSuccessFinishResult); +    return true; +  } +}; + +class CommandObjectRenderScriptScriptGroup : public CommandObjectMultiword { +public: +  CommandObjectRenderScriptScriptGroup(CommandInterpreter &interpreter) +      : CommandObjectMultiword(interpreter, "renderscript scriptgroup", +                               "Command set for interacting with scriptgroups.", +                               nullptr, eCommandRequiresProcess | +                                            eCommandProcessMustBeLaunched) { +    LoadSubCommand( +        "breakpoint", +        CommandObjectSP( +            new CommandObjectRenderScriptScriptGroupBreakpoint(interpreter))); +    LoadSubCommand( +        "list", CommandObjectSP( +                    new CommandObjectRenderScriptScriptGroupList(interpreter))); +  } + +  ~CommandObjectRenderScriptScriptGroup() override = default; +}; + +lldb::CommandObjectSP NewCommandObjectRenderScriptScriptGroup( +    lldb_private::CommandInterpreter &interpreter) { +  return CommandObjectSP(new CommandObjectRenderScriptScriptGroup(interpreter)); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h new file mode 100644 index 000000000000..c25e240f6d52 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptScriptGroup.h @@ -0,0 +1,17 @@ +//===-- RenderScriptScriptGroup.h -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RenderScriptScriptGroup_h_ +#define liblldb_RenderScriptScriptGroup_h_ + +#include "lldb/Interpreter/CommandInterpreter.h" + +lldb::CommandObjectSP NewCommandObjectRenderScriptScriptGroup( +    lldb_private::CommandInterpreter &interpreter); + +#endif // liblldb_RenderScriptScriptGroup_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp new file mode 100644 index 000000000000..4725e8c5b0eb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp @@ -0,0 +1,281 @@ +//===-- RenderScriptx86ABIFixups.cpp ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <set> + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Pass.h" + +#include "lldb/Target/Process.h" +#include "lldb/Utility/Log.h" + +using namespace lldb_private; +namespace { + +bool isRSAPICall(llvm::Module &module, llvm::CallInst *call_inst) { +  // TODO get the list of renderscript modules from lldb and check if +  // this llvm::Module calls into any of them. +  (void)module; +  const auto func_name = call_inst->getCalledFunction()->getName(); +  if (func_name.startswith("llvm") || func_name.startswith("lldb")) +    return false; + +  if (call_inst->getCalledFunction()->isIntrinsic()) +    return false; + +  return true; +} + +bool isRSLargeReturnCall(llvm::Module &module, llvm::CallInst *call_inst) { +  // i686 and x86_64 returns for large vectors in the RenderScript API are not +  // handled as normal register pairs, but as a hidden sret type. This is not +  // reflected in the debug info or mangled symbol name, and the android ABI +  // for x86 and x86_64, (as well as the emulators) specifies there is no AVX, +  // so bcc generates an sret function because we cannot natively return +  // 256 bit vectors. +  // This function simply checks whether a function has a > 128bit return type. +  // It is perhaps an unreliable heuristic, and relies on bcc not generating +  // AVX code, so if the android ABI one day provides for AVX, this function +  // may go out of fashion. +  (void)module; +  if (!call_inst || !call_inst->getCalledFunction()) +    return false; + +  return call_inst->getCalledFunction() +             ->getReturnType() +             ->getPrimitiveSizeInBits() > 128; +} + +bool isRSAllocationPtrTy(const llvm::Type *type) { +  if (!type->isPointerTy()) +    return false; +  auto ptr_type = type->getPointerElementType(); + +  return ptr_type->isStructTy() && +         ptr_type->getStructName().startswith("struct.rs_allocation"); +} + +bool isRSAllocationTyCallSite(llvm::Module &module, llvm::CallInst *call_inst) { +  (void)module; +  if (!call_inst->hasByValArgument()) +    return false; +  for (const auto ¶m : call_inst->operand_values()) +    if (isRSAllocationPtrTy(param->getType())) +      return true; +  return false; +} + +llvm::FunctionType *cloneToStructRetFnTy(llvm::CallInst *call_inst) { +  // on x86 StructReturn functions return a pointer to the return value, rather +  // than the return value itself +  // [ref](http://www.agner.org/optimize/calling_conventions.pdf section 6). We +  // create a return type by getting the pointer type of the old return type, +  // and inserting a new initial argument of pointer type of the original +  // return type. +  Log *log( +      GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS)); + +  assert(call_inst && "no CallInst"); +  llvm::Function *orig = call_inst->getCalledFunction(); +  assert(orig && "CallInst has no called function"); +  llvm::FunctionType *orig_type = orig->getFunctionType(); +  auto name = orig->getName(); +  if (log) +    log->Printf("%s - cloning to StructRet function for '%s'", __FUNCTION__, +                name.str().c_str()); + +  unsigned num_params = orig_type->getNumParams(); +  std::vector<llvm::Type *> new_params{num_params + 1, nullptr}; +  std::vector<llvm::Type *> params{orig_type->param_begin(), +                                   orig_type->param_end()}; + +  // This may not work if the function is somehow declared void as llvm is +  // strongly typed and represents void* with i8* +  assert(!orig_type->getReturnType()->isVoidTy() && +         "Cannot add StructRet attribute to void function"); +  llvm::PointerType *return_type_ptr_type = +      llvm::PointerType::getUnqual(orig->getReturnType()); +  assert(return_type_ptr_type && +         "failed to get function return type PointerType"); +  if (!return_type_ptr_type) +    return nullptr; + +  if (log) +    log->Printf("%s - return type pointer type for StructRet clone @ '0x%p':\n", +                __FUNCTION__, (void *)return_type_ptr_type); +  // put the sret pointer argument in place at the beginning of the +  // argument list. +  params.emplace(params.begin(), return_type_ptr_type); +  assert(params.size() == num_params + 1); +  return llvm::FunctionType::get(return_type_ptr_type, params, +                                 orig->isVarArg()); +} + +bool findRSCallSites(llvm::Module &module, +                     std::set<llvm::CallInst *> &rs_callsites, +                     bool (*predicate)(llvm::Module &, llvm::CallInst *)) { +  bool found = false; + +  for (auto &func : module.getFunctionList()) +    for (auto &block : func.getBasicBlockList()) +      for (auto &inst : block) { +        llvm::CallInst *call_inst = +            llvm::dyn_cast_or_null<llvm::CallInst>(&inst); +        if (!call_inst || !call_inst->getCalledFunction()) +          // This is not the call-site you are looking for... +          continue; +        if (isRSAPICall(module, call_inst) && predicate(module, call_inst)) { +          rs_callsites.insert(call_inst); +          found = true; +        } +      } +  return found; +} + +bool fixupX86StructRetCalls(llvm::Module &module) { +  bool changed = false; +  // changing a basic block while iterating over it seems to have some +  // undefined behaviour going on so we find all RS callsites first, then fix +  // them up after consuming the iterator. +  std::set<llvm::CallInst *> rs_callsites; +  if (!findRSCallSites(module, rs_callsites, isRSLargeReturnCall)) +    return false; + +  for (auto call_inst : rs_callsites) { +    llvm::FunctionType *new_func_type = cloneToStructRetFnTy(call_inst); +    assert(new_func_type && +           "failed to clone functionType for Renderscript ABI fixup"); + +    llvm::CallSite call_site(call_inst); +    llvm::Function *func = call_inst->getCalledFunction(); +    assert(func && "cannot resolve function in RenderScriptRuntime"); +    // Copy the original call arguments +    std::vector<llvm::Value *> new_call_args(call_site.arg_begin(), +                                             call_site.arg_end()); + +    // Allocate enough space to store the return value of the original function +    // we pass a pointer to this allocation as the StructRet param, and then +    // copy its value into the lldb return value +    const llvm::DataLayout &DL = module.getDataLayout(); +    llvm::AllocaInst *return_value_alloc = new llvm::AllocaInst( +      func->getReturnType(), DL.getAllocaAddrSpace(), "var_vector_return_alloc", +      call_inst); +    // use the new allocation as the new first argument +    new_call_args.emplace(new_call_args.begin(), +                          llvm::cast<llvm::Value>(return_value_alloc)); +    llvm::PointerType *new_func_ptr_type = +        llvm::PointerType::get(new_func_type, 0); +    // Create the type cast from the old function type to the new one +    llvm::Constant *new_func_cast = llvm::ConstantExpr::getCast( +        llvm::Instruction::BitCast, func, new_func_ptr_type); +    // create an allocation for a new function pointer +    llvm::AllocaInst *new_func_ptr = +        new llvm::AllocaInst(new_func_ptr_type, DL.getAllocaAddrSpace(), +                             "new_func_ptr", call_inst); +    // store the new_func_cast to the newly allocated space +    (new llvm::StoreInst(new_func_cast, new_func_ptr, call_inst)) +        ->setName("new_func_ptr_load_cast"); +    // load the new function address ready for a jump +    llvm::LoadInst *new_func_addr_load = +        new llvm::LoadInst(new_func_ptr, "load_func_pointer", call_inst); +    // and create a callinstruction from it +    llvm::CallInst *new_call_inst = +        llvm::CallInst::Create(new_func_type, new_func_addr_load, new_call_args, +                               "new_func_call", call_inst); +    new_call_inst->setCallingConv(call_inst->getCallingConv()); +    new_call_inst->setTailCall(call_inst->isTailCall()); +    llvm::LoadInst *lldb_save_result_address = +        new llvm::LoadInst(return_value_alloc, "save_return_val", call_inst); + +    // Now remove the old broken call +    call_inst->replaceAllUsesWith(lldb_save_result_address); +    call_inst->eraseFromParent(); +    changed = true; +  } +  return changed; +} + +bool fixupRSAllocationStructByValCalls(llvm::Module &module) { +  // On x86_64, calls to functions in the RS runtime that take an +  // `rs_allocation` type argument are actually handled as by-ref params by +  // bcc, but appear to be passed by value by lldb (the callsite all use +  // `struct byval`). On x86_64 Linux, struct arguments are transferred in +  // registers if the struct size is no bigger than 128bits +  // [ref](http://www.agner.org/optimize/calling_conventions.pdf) section 7.1 +  // "Passing and returning objects" otherwise passed on the stack. an object +  // of type `rs_allocation` is actually 256bits, so should be passed on the +  // stack. However, code generated by bcc actually treats formal params of +  // type `rs_allocation` as `rs_allocation *` so we need to convert the +  // calling convention to pass by reference, and remove any hint of byval from +  // formal parameters. +  bool changed = false; +  std::set<llvm::CallInst *> rs_callsites; +  if (!findRSCallSites(module, rs_callsites, isRSAllocationTyCallSite)) +    return false; + +  std::set<llvm::Function *> rs_functions; + +  // for all call instructions +  for (auto call_inst : rs_callsites) { +    // add the called function to a set so that we can strip its byval +    // attributes in another pass +    rs_functions.insert(call_inst->getCalledFunction()); + +    // get the function attributes +    llvm::AttributeList call_attribs = call_inst->getAttributes(); + +    // iterate over the argument attributes +    for (unsigned I = call_attribs.index_begin(); I != call_attribs.index_end(); +         I++) { +      // if this argument is passed by val +      if (call_attribs.hasAttribute(I, llvm::Attribute::ByVal)) { +        // strip away the byval attribute +        call_inst->removeAttribute(I, llvm::Attribute::ByVal); +        changed = true; +      } +    } +  } + +  // for all called function decls +  for (auto func : rs_functions) { +    // inspect all of the arguments in the call +    for (auto &arg : func->args()) { +      if (arg.hasByValAttr()) { +        arg.removeAttr(llvm::Attribute::ByVal); +        changed = true; +      } +    } +  } +  return changed; +} +} // end anonymous namespace + +namespace lldb_private { +namespace lldb_renderscript { + +bool fixupX86FunctionCalls(llvm::Module &module) { +  return fixupX86StructRetCalls(module); +} + +bool fixupX86_64FunctionCalls(llvm::Module &module) { +  bool changed = false; +  changed |= fixupX86StructRetCalls(module); +  changed |= fixupRSAllocationStructByValCalls(module); +  return changed; +} + +} // end namespace lldb_renderscript +} // end namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h new file mode 100644 index 000000000000..a5efc999aea4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.h @@ -0,0 +1,22 @@ +//===-- RenderScriptx86ABIFixups.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_RENDERSCRIPT_X86_H +#define LLDB_RENDERSCRIPT_X86_H + +#include "llvm/IR/Module.h" + +namespace lldb_private { +namespace lldb_renderscript { + +bool fixupX86FunctionCalls(llvm::Module &module); + +bool fixupX86_64FunctionCalls(llvm::Module &module); +} +} +#endif | 
