diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectMemory.cpp')
| -rw-r--r-- | lldb/source/Commands/CommandObjectMemory.cpp | 1786 | 
1 files changed, 1786 insertions, 0 deletions
diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp new file mode 100644 index 0000000000000..38bd3d1790969 --- /dev/null +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -0,0 +1,1786 @@ +//===-- CommandObjectMemory.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 "CommandObjectMemory.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/DumpDataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/DataFormatters/ValueObjectPrinter.h" +#include "lldb/Expression/ExpressionVariable.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupOutputFile.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Interpreter/OptionValueLanguage.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/MemoryHistory.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataBufferLLVM.h" +#include "lldb/Utility/StreamString.h" + +#include "lldb/lldb-private.h" + +#include <cinttypes> +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +#define LLDB_OPTIONS_memory_read +#include "CommandOptions.inc" + +class OptionGroupReadMemory : public OptionGroup { +public: +  OptionGroupReadMemory() +      : m_num_per_line(1, 1), m_output_as_binary(false), m_view_as_type(), +        m_offset(0, 0), m_language_for_type(eLanguageTypeUnknown) {} + +  ~OptionGroupReadMemory() override = default; + +  llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +    return llvm::makeArrayRef(g_memory_read_options); +  } + +  Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, +                        ExecutionContext *execution_context) override { +    Status error; +    const int short_option = g_memory_read_options[option_idx].short_option; + +    switch (short_option) { +    case 'l': +      error = m_num_per_line.SetValueFromString(option_value); +      if (m_num_per_line.GetCurrentValue() == 0) +        error.SetErrorStringWithFormat( +            "invalid value for --num-per-line option '%s'", +            option_value.str().c_str()); +      break; + +    case 'b': +      m_output_as_binary = true; +      break; + +    case 't': +      error = m_view_as_type.SetValueFromString(option_value); +      break; + +    case 'r': +      m_force = true; +      break; + +    case 'x': +      error = m_language_for_type.SetValueFromString(option_value); +      break; + +    case 'E': +      error = m_offset.SetValueFromString(option_value); +      break; + +    default: +      llvm_unreachable("Unimplemented option"); +    } +    return error; +  } + +  void OptionParsingStarting(ExecutionContext *execution_context) override { +    m_num_per_line.Clear(); +    m_output_as_binary = false; +    m_view_as_type.Clear(); +    m_force = false; +    m_offset.Clear(); +    m_language_for_type.Clear(); +  } + +  Status FinalizeSettings(Target *target, OptionGroupFormat &format_options) { +    Status error; +    OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue(); +    OptionValueUInt64 &count_value = format_options.GetCountValue(); +    const bool byte_size_option_set = byte_size_value.OptionWasSet(); +    const bool num_per_line_option_set = m_num_per_line.OptionWasSet(); +    const bool count_option_set = format_options.GetCountValue().OptionWasSet(); + +    switch (format_options.GetFormat()) { +    default: +      break; + +    case eFormatBoolean: +      if (!byte_size_option_set) +        byte_size_value = 1; +      if (!num_per_line_option_set) +        m_num_per_line = 1; +      if (!count_option_set) +        format_options.GetCountValue() = 8; +      break; + +    case eFormatCString: +      break; + +    case eFormatInstruction: +      if (count_option_set) +        byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize(); +      m_num_per_line = 1; +      break; + +    case eFormatAddressInfo: +      if (!byte_size_option_set) +        byte_size_value = target->GetArchitecture().GetAddressByteSize(); +      m_num_per_line = 1; +      if (!count_option_set) +        format_options.GetCountValue() = 8; +      break; + +    case eFormatPointer: +      byte_size_value = target->GetArchitecture().GetAddressByteSize(); +      if (!num_per_line_option_set) +        m_num_per_line = 4; +      if (!count_option_set) +        format_options.GetCountValue() = 8; +      break; + +    case eFormatBinary: +    case eFormatFloat: +    case eFormatOctal: +    case eFormatDecimal: +    case eFormatEnum: +    case eFormatUnicode8: +    case eFormatUnicode16: +    case eFormatUnicode32: +    case eFormatUnsigned: +    case eFormatHexFloat: +      if (!byte_size_option_set) +        byte_size_value = 4; +      if (!num_per_line_option_set) +        m_num_per_line = 1; +      if (!count_option_set) +        format_options.GetCountValue() = 8; +      break; + +    case eFormatBytes: +    case eFormatBytesWithASCII: +      if (byte_size_option_set) { +        if (byte_size_value > 1) +          error.SetErrorStringWithFormat( +              "display format (bytes/bytes with ASCII) conflicts with the " +              "specified byte size %" PRIu64 "\n" +              "\tconsider using a different display format or don't specify " +              "the byte size.", +              byte_size_value.GetCurrentValue()); +      } else +        byte_size_value = 1; +      if (!num_per_line_option_set) +        m_num_per_line = 16; +      if (!count_option_set) +        format_options.GetCountValue() = 32; +      break; + +    case eFormatCharArray: +    case eFormatChar: +    case eFormatCharPrintable: +      if (!byte_size_option_set) +        byte_size_value = 1; +      if (!num_per_line_option_set) +        m_num_per_line = 32; +      if (!count_option_set) +        format_options.GetCountValue() = 64; +      break; + +    case eFormatComplex: +      if (!byte_size_option_set) +        byte_size_value = 8; +      if (!num_per_line_option_set) +        m_num_per_line = 1; +      if (!count_option_set) +        format_options.GetCountValue() = 8; +      break; + +    case eFormatComplexInteger: +      if (!byte_size_option_set) +        byte_size_value = 8; +      if (!num_per_line_option_set) +        m_num_per_line = 1; +      if (!count_option_set) +        format_options.GetCountValue() = 8; +      break; + +    case eFormatHex: +      if (!byte_size_option_set) +        byte_size_value = 4; +      if (!num_per_line_option_set) { +        switch (byte_size_value) { +        case 1: +        case 2: +          m_num_per_line = 8; +          break; +        case 4: +          m_num_per_line = 4; +          break; +        case 8: +          m_num_per_line = 2; +          break; +        default: +          m_num_per_line = 1; +          break; +        } +      } +      if (!count_option_set) +        count_value = 8; +      break; + +    case eFormatVectorOfChar: +    case eFormatVectorOfSInt8: +    case eFormatVectorOfUInt8: +    case eFormatVectorOfSInt16: +    case eFormatVectorOfUInt16: +    case eFormatVectorOfSInt32: +    case eFormatVectorOfUInt32: +    case eFormatVectorOfSInt64: +    case eFormatVectorOfUInt64: +    case eFormatVectorOfFloat16: +    case eFormatVectorOfFloat32: +    case eFormatVectorOfFloat64: +    case eFormatVectorOfUInt128: +      if (!byte_size_option_set) +        byte_size_value = 128; +      if (!num_per_line_option_set) +        m_num_per_line = 1; +      if (!count_option_set) +        count_value = 4; +      break; +    } +    return error; +  } + +  bool AnyOptionWasSet() const { +    return m_num_per_line.OptionWasSet() || m_output_as_binary || +           m_view_as_type.OptionWasSet() || m_offset.OptionWasSet() || +           m_language_for_type.OptionWasSet(); +  } + +  OptionValueUInt64 m_num_per_line; +  bool m_output_as_binary; +  OptionValueString m_view_as_type; +  bool m_force; +  OptionValueUInt64 m_offset; +  OptionValueLanguage m_language_for_type; +}; + +// Read memory from the inferior process +class CommandObjectMemoryRead : public CommandObjectParsed { +public: +  CommandObjectMemoryRead(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "memory read", +            "Read from the memory of the current target process.", nullptr, +            eCommandRequiresTarget | eCommandProcessMustBePaused), +        m_option_group(), m_format_options(eFormatBytesWithASCII, 1, 8), +        m_memory_options(), m_outfile_options(), m_varobj_options(), +        m_next_addr(LLDB_INVALID_ADDRESS), m_prev_byte_size(0), +        m_prev_format_options(eFormatBytesWithASCII, 1, 8), +        m_prev_memory_options(), m_prev_outfile_options(), +        m_prev_varobj_options() { +    CommandArgumentEntry arg1; +    CommandArgumentEntry arg2; +    CommandArgumentData start_addr_arg; +    CommandArgumentData end_addr_arg; + +    // Define the first (and only) variant of this arg. +    start_addr_arg.arg_type = eArgTypeAddressOrExpression; +    start_addr_arg.arg_repetition = eArgRepeatPlain; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg1.push_back(start_addr_arg); + +    // Define the first (and only) variant of this arg. +    end_addr_arg.arg_type = eArgTypeAddressOrExpression; +    end_addr_arg.arg_repetition = eArgRepeatOptional; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg2.push_back(end_addr_arg); + +    // Push the data for the first argument into the m_arguments vector. +    m_arguments.push_back(arg1); +    m_arguments.push_back(arg2); + +    // Add the "--format" and "--count" options to group 1 and 3 +    m_option_group.Append(&m_format_options, +                          OptionGroupFormat::OPTION_GROUP_FORMAT | +                              OptionGroupFormat::OPTION_GROUP_COUNT, +                          LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); +    m_option_group.Append(&m_format_options, +                          OptionGroupFormat::OPTION_GROUP_GDB_FMT, +                          LLDB_OPT_SET_1 | LLDB_OPT_SET_3); +    // Add the "--size" option to group 1 and 2 +    m_option_group.Append(&m_format_options, +                          OptionGroupFormat::OPTION_GROUP_SIZE, +                          LLDB_OPT_SET_1 | LLDB_OPT_SET_2); +    m_option_group.Append(&m_memory_options); +    m_option_group.Append(&m_outfile_options, LLDB_OPT_SET_ALL, +                          LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); +    m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3); +    m_option_group.Finalize(); +  } + +  ~CommandObjectMemoryRead() override = default; + +  Options *GetOptions() override { return &m_option_group; } + +  const char *GetRepeatCommand(Args ¤t_command_args, +                               uint32_t index) override { +    return m_cmd_name.c_str(); +  } + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    // No need to check "target" for validity as eCommandRequiresTarget ensures +    // it is valid +    Target *target = m_exe_ctx.GetTargetPtr(); + +    const size_t argc = command.GetArgumentCount(); + +    if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2) { +      result.AppendErrorWithFormat("%s takes a start address expression with " +                                   "an optional end address expression.\n", +                                   m_cmd_name.c_str()); +      result.AppendRawWarning("Expressions should be quoted if they contain " +                              "spaces or other special characters.\n"); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    CompilerType compiler_type; +    Status error; + +    const char *view_as_type_cstr = +        m_memory_options.m_view_as_type.GetCurrentValue(); +    if (view_as_type_cstr && view_as_type_cstr[0]) { +      // We are viewing memory as a type + +      const bool exact_match = false; +      TypeList type_list; +      uint32_t reference_count = 0; +      uint32_t pointer_count = 0; +      size_t idx; + +#define ALL_KEYWORDS                                                           \ +  KEYWORD("const")                                                             \ +  KEYWORD("volatile")                                                          \ +  KEYWORD("restrict")                                                          \ +  KEYWORD("struct")                                                            \ +  KEYWORD("class")                                                             \ +  KEYWORD("union") + +#define KEYWORD(s) s, +      static const char *g_keywords[] = {ALL_KEYWORDS}; +#undef KEYWORD + +#define KEYWORD(s) (sizeof(s) - 1), +      static const int g_keyword_lengths[] = {ALL_KEYWORDS}; +#undef KEYWORD + +#undef ALL_KEYWORDS + +      static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *); +      std::string type_str(view_as_type_cstr); + +      // Remove all instances of g_keywords that are followed by spaces +      for (size_t i = 0; i < g_num_keywords; ++i) { +        const char *keyword = g_keywords[i]; +        int keyword_len = g_keyword_lengths[i]; + +        idx = 0; +        while ((idx = type_str.find(keyword, idx)) != std::string::npos) { +          if (type_str[idx + keyword_len] == ' ' || +              type_str[idx + keyword_len] == '\t') { +            type_str.erase(idx, keyword_len + 1); +            idx = 0; +          } else { +            idx += keyword_len; +          } +        } +      } +      bool done = type_str.empty(); +      // +      idx = type_str.find_first_not_of(" \t"); +      if (idx > 0 && idx != std::string::npos) +        type_str.erase(0, idx); +      while (!done) { +        // Strip trailing spaces +        if (type_str.empty()) +          done = true; +        else { +          switch (type_str[type_str.size() - 1]) { +          case '*': +            ++pointer_count; +            LLVM_FALLTHROUGH; +          case ' ': +          case '\t': +            type_str.erase(type_str.size() - 1); +            break; + +          case '&': +            if (reference_count == 0) { +              reference_count = 1; +              type_str.erase(type_str.size() - 1); +            } else { +              result.AppendErrorWithFormat("invalid type string: '%s'\n", +                                           view_as_type_cstr); +              result.SetStatus(eReturnStatusFailed); +              return false; +            } +            break; + +          default: +            done = true; +            break; +          } +        } +      } + +      llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files; +      ConstString lookup_type_name(type_str.c_str()); +      StackFrame *frame = m_exe_ctx.GetFramePtr(); +      ModuleSP search_first; +      if (frame) { +        search_first = frame->GetSymbolContext(eSymbolContextModule).module_sp; +      } +      target->GetImages().FindTypes(search_first.get(), lookup_type_name, +                                    exact_match, 1, searched_symbol_files, +                                    type_list); + +      if (type_list.GetSize() == 0 && lookup_type_name.GetCString()) { +        LanguageType language_for_type = +            m_memory_options.m_language_for_type.GetCurrentValue(); +        std::set<LanguageType> languages_to_check; +        if (language_for_type != eLanguageTypeUnknown) { +          languages_to_check.insert(language_for_type); +        } else { +          languages_to_check = Language::GetSupportedLanguages(); +        } + +        std::set<CompilerType> user_defined_types; +        for (auto lang : languages_to_check) { +          if (auto *persistent_vars = +                  target->GetPersistentExpressionStateForLanguage(lang)) { +            if (llvm::Optional<CompilerType> type = +                    persistent_vars->GetCompilerTypeFromPersistentDecl( +                        lookup_type_name)) { +              user_defined_types.emplace(*type); +            } +          } +        } + +        if (user_defined_types.size() > 1) { +          result.AppendErrorWithFormat( +              "Mutiple types found matching raw type '%s', please disambiguate " +              "by specifying the language with -x", +              lookup_type_name.GetCString()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } + +        if (user_defined_types.size() == 1) { +          compiler_type = *user_defined_types.begin(); +        } +      } + +      if (!compiler_type.IsValid()) { +        if (type_list.GetSize() == 0) { +          result.AppendErrorWithFormat("unable to find any types that match " +                                       "the raw type '%s' for full type '%s'\n", +                                       lookup_type_name.GetCString(), +                                       view_as_type_cstr); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } else { +          TypeSP type_sp(type_list.GetTypeAtIndex(0)); +          compiler_type = type_sp->GetFullCompilerType(); +        } +      } + +      while (pointer_count > 0) { +        CompilerType pointer_type = compiler_type.GetPointerType(); +        if (pointer_type.IsValid()) +          compiler_type = pointer_type; +        else { +          result.AppendError("unable make a pointer type\n"); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +        --pointer_count; +      } + +      llvm::Optional<uint64_t> size = compiler_type.GetByteSize(nullptr); +      if (!size) { +        result.AppendErrorWithFormat( +            "unable to get the byte size of the type '%s'\n", +            view_as_type_cstr); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +      m_format_options.GetByteSizeValue() = *size; + +      if (!m_format_options.GetCountValue().OptionWasSet()) +        m_format_options.GetCountValue() = 1; +    } else { +      error = m_memory_options.FinalizeSettings(target, m_format_options); +    } + +    // Look for invalid combinations of settings +    if (error.Fail()) { +      result.AppendError(error.AsCString()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    lldb::addr_t addr; +    size_t total_byte_size = 0; +    if (argc == 0) { +      // Use the last address and byte size and all options as they were if no +      // options have been set +      addr = m_next_addr; +      total_byte_size = m_prev_byte_size; +      compiler_type = m_prev_compiler_type; +      if (!m_format_options.AnyOptionWasSet() && +          !m_memory_options.AnyOptionWasSet() && +          !m_outfile_options.AnyOptionWasSet() && +          !m_varobj_options.AnyOptionWasSet()) { +        m_format_options = m_prev_format_options; +        m_memory_options = m_prev_memory_options; +        m_outfile_options = m_prev_outfile_options; +        m_varobj_options = m_prev_varobj_options; +      } +    } + +    size_t item_count = m_format_options.GetCountValue().GetCurrentValue(); + +    // TODO For non-8-bit byte addressable architectures this needs to be +    // revisited to fully support all lldb's range of formatting options. +    // Furthermore code memory reads (for those architectures) will not be +    // correctly formatted even w/o formatting options. +    size_t item_byte_size = +        target->GetArchitecture().GetDataByteSize() > 1 +            ? target->GetArchitecture().GetDataByteSize() +            : m_format_options.GetByteSizeValue().GetCurrentValue(); + +    const size_t num_per_line = +        m_memory_options.m_num_per_line.GetCurrentValue(); + +    if (total_byte_size == 0) { +      total_byte_size = item_count * item_byte_size; +      if (total_byte_size == 0) +        total_byte_size = 32; +    } + +    if (argc > 0) +      addr = OptionArgParser::ToAddress(&m_exe_ctx, command[0].ref(), +                                        LLDB_INVALID_ADDRESS, &error); + +    if (addr == LLDB_INVALID_ADDRESS) { +      result.AppendError("invalid start address expression."); +      result.AppendError(error.AsCString()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    if (argc == 2) { +      lldb::addr_t end_addr = OptionArgParser::ToAddress( +          &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, nullptr); +      if (end_addr == LLDB_INVALID_ADDRESS) { +        result.AppendError("invalid end address expression."); +        result.AppendError(error.AsCString()); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } else if (end_addr <= addr) { +        result.AppendErrorWithFormat( +            "end address (0x%" PRIx64 +            ") must be greater that the start address (0x%" PRIx64 ").\n", +            end_addr, addr); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } else if (m_format_options.GetCountValue().OptionWasSet()) { +        result.AppendErrorWithFormat( +            "specify either the end address (0x%" PRIx64 +            ") or the count (--count %" PRIu64 "), not both.\n", +            end_addr, (uint64_t)item_count); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } + +      total_byte_size = end_addr - addr; +      item_count = total_byte_size / item_byte_size; +    } + +    uint32_t max_unforced_size = target->GetMaximumMemReadSize(); + +    if (total_byte_size > max_unforced_size && !m_memory_options.m_force) { +      result.AppendErrorWithFormat( +          "Normally, \'memory read\' will not read over %" PRIu32 +          " bytes of data.\n", +          max_unforced_size); +      result.AppendErrorWithFormat( +          "Please use --force to override this restriction just once.\n"); +      result.AppendErrorWithFormat("or set target.max-memory-read-size if you " +                                   "will often need a larger limit.\n"); +      return false; +    } + +    DataBufferSP data_sp; +    size_t bytes_read = 0; +    if (compiler_type.GetOpaqueQualType()) { +      // Make sure we don't display our type as ASCII bytes like the default +      // memory read +      if (!m_format_options.GetFormatValue().OptionWasSet()) +        m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault); + +      llvm::Optional<uint64_t> size = compiler_type.GetByteSize(nullptr); +      if (!size) { +        result.AppendError("can't get size of type"); +        return false; +      } +      bytes_read = *size * m_format_options.GetCountValue().GetCurrentValue(); + +      if (argc > 0) +        addr = addr + (*size * m_memory_options.m_offset.GetCurrentValue()); +    } else if (m_format_options.GetFormatValue().GetCurrentValue() != +               eFormatCString) { +      data_sp = std::make_shared<DataBufferHeap>(total_byte_size, '\0'); +      if (data_sp->GetBytes() == nullptr) { +        result.AppendErrorWithFormat( +            "can't allocate 0x%" PRIx32 +            " bytes for the memory read buffer, specify a smaller size to read", +            (uint32_t)total_byte_size); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } + +      Address address(addr, nullptr); +      bytes_read = target->ReadMemory(address, false, data_sp->GetBytes(), +                                      data_sp->GetByteSize(), error); +      if (bytes_read == 0) { +        const char *error_cstr = error.AsCString(); +        if (error_cstr && error_cstr[0]) { +          result.AppendError(error_cstr); +        } else { +          result.AppendErrorWithFormat( +              "failed to read memory from 0x%" PRIx64 ".\n", addr); +        } +        result.SetStatus(eReturnStatusFailed); +        return false; +      } + +      if (bytes_read < total_byte_size) +        result.AppendWarningWithFormat( +            "Not all bytes (%" PRIu64 "/%" PRIu64 +            ") were able to be read from 0x%" PRIx64 ".\n", +            (uint64_t)bytes_read, (uint64_t)total_byte_size, addr); +    } else { +      // we treat c-strings as a special case because they do not have a fixed +      // size +      if (m_format_options.GetByteSizeValue().OptionWasSet() && +          !m_format_options.HasGDBFormat()) +        item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue(); +      else +        item_byte_size = target->GetMaximumSizeOfStringSummary(); +      if (!m_format_options.GetCountValue().OptionWasSet()) +        item_count = 1; +      data_sp = std::make_shared<DataBufferHeap>( +          (item_byte_size + 1) * item_count, +          '\0'); // account for NULLs as necessary +      if (data_sp->GetBytes() == nullptr) { +        result.AppendErrorWithFormat( +            "can't allocate 0x%" PRIx64 +            " bytes for the memory read buffer, specify a smaller size to read", +            (uint64_t)((item_byte_size + 1) * item_count)); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +      uint8_t *data_ptr = data_sp->GetBytes(); +      auto data_addr = addr; +      auto count = item_count; +      item_count = 0; +      bool break_on_no_NULL = false; +      while (item_count < count) { +        std::string buffer; +        buffer.resize(item_byte_size + 1, 0); +        Status error; +        size_t read = target->ReadCStringFromMemory(data_addr, &buffer[0], +                                                    item_byte_size + 1, error); +        if (error.Fail()) { +          result.AppendErrorWithFormat( +              "failed to read memory from 0x%" PRIx64 ".\n", addr); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } + +        if (item_byte_size == read) { +          result.AppendWarningWithFormat( +              "unable to find a NULL terminated string at 0x%" PRIx64 +              ".Consider increasing the maximum read length.\n", +              data_addr); +          --read; +          break_on_no_NULL = true; +        } else +          ++read; // account for final NULL byte + +        memcpy(data_ptr, &buffer[0], read); +        data_ptr += read; +        data_addr += read; +        bytes_read += read; +        item_count++; // if we break early we know we only read item_count +                      // strings + +        if (break_on_no_NULL) +          break; +      } +      data_sp = +          std::make_shared<DataBufferHeap>(data_sp->GetBytes(), bytes_read + 1); +    } + +    m_next_addr = addr + bytes_read; +    m_prev_byte_size = bytes_read; +    m_prev_format_options = m_format_options; +    m_prev_memory_options = m_memory_options; +    m_prev_outfile_options = m_outfile_options; +    m_prev_varobj_options = m_varobj_options; +    m_prev_compiler_type = compiler_type; + +    std::unique_ptr<Stream> output_stream_storage; +    Stream *output_stream_p = nullptr; +    const FileSpec &outfile_spec = +        m_outfile_options.GetFile().GetCurrentValue(); + +    std::string path = outfile_spec.GetPath(); +    if (outfile_spec) { + +      auto open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate; +      const bool append = m_outfile_options.GetAppend().GetCurrentValue(); +      if (append) +        open_options |= File::eOpenOptionAppend; + +      auto outfile = FileSystem::Instance().Open(outfile_spec, open_options); + +      if (outfile) { +        auto outfile_stream_up = +            std::make_unique<StreamFile>(std::move(outfile.get())); +        if (m_memory_options.m_output_as_binary) { +          const size_t bytes_written = +              outfile_stream_up->Write(data_sp->GetBytes(), bytes_read); +          if (bytes_written > 0) { +            result.GetOutputStream().Printf( +                "%zi bytes %s to '%s'\n", bytes_written, +                append ? "appended" : "written", path.c_str()); +            return true; +          } else { +            result.AppendErrorWithFormat("Failed to write %" PRIu64 +                                         " bytes to '%s'.\n", +                                         (uint64_t)bytes_read, path.c_str()); +            result.SetStatus(eReturnStatusFailed); +            return false; +          } +        } else { +          // We are going to write ASCII to the file just point the +          // output_stream to our outfile_stream... +          output_stream_storage = std::move(outfile_stream_up); +          output_stream_p = output_stream_storage.get(); +        } +      } else { +        result.AppendErrorWithFormat("Failed to open file '%s' for %s:\n", +                                     path.c_str(), append ? "append" : "write"); + +        result.AppendError(llvm::toString(outfile.takeError())); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +    } else { +      output_stream_p = &result.GetOutputStream(); +    } + +    ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); +    if (compiler_type.GetOpaqueQualType()) { +      for (uint32_t i = 0; i < item_count; ++i) { +        addr_t item_addr = addr + (i * item_byte_size); +        Address address(item_addr); +        StreamString name_strm; +        name_strm.Printf("0x%" PRIx64, item_addr); +        ValueObjectSP valobj_sp(ValueObjectMemory::Create( +            exe_scope, name_strm.GetString(), address, compiler_type)); +        if (valobj_sp) { +          Format format = m_format_options.GetFormat(); +          if (format != eFormatDefault) +            valobj_sp->SetFormat(format); + +          DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( +              eLanguageRuntimeDescriptionDisplayVerbosityFull, format)); + +          valobj_sp->Dump(*output_stream_p, options); +        } else { +          result.AppendErrorWithFormat( +              "failed to create a value object for: (%s) %s\n", +              view_as_type_cstr, name_strm.GetData()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +      } +      return true; +    } + +    result.SetStatus(eReturnStatusSuccessFinishResult); +    DataExtractor data(data_sp, target->GetArchitecture().GetByteOrder(), +                       target->GetArchitecture().GetAddressByteSize(), +                       target->GetArchitecture().GetDataByteSize()); + +    Format format = m_format_options.GetFormat(); +    if (((format == eFormatChar) || (format == eFormatCharPrintable)) && +        (item_byte_size != 1)) { +      // if a count was not passed, or it is 1 +      if (!m_format_options.GetCountValue().OptionWasSet() || item_count == 1) { +        // this turns requests such as +        // memory read -fc -s10 -c1 *charPtrPtr +        // which make no sense (what is a char of size 10?) into a request for +        // fetching 10 chars of size 1 from the same memory location +        format = eFormatCharArray; +        item_count = item_byte_size; +        item_byte_size = 1; +      } else { +        // here we passed a count, and it was not 1 so we have a byte_size and +        // a count we could well multiply those, but instead let's just fail +        result.AppendErrorWithFormat( +            "reading memory as characters of size %" PRIu64 " is not supported", +            (uint64_t)item_byte_size); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +    } + +    assert(output_stream_p); +    size_t bytes_dumped = DumpDataExtractor( +        data, output_stream_p, 0, format, item_byte_size, item_count, +        num_per_line / target->GetArchitecture().GetDataByteSize(), addr, 0, 0, +        exe_scope); +    m_next_addr = addr + bytes_dumped; +    output_stream_p->EOL(); +    return true; +  } + +  OptionGroupOptions m_option_group; +  OptionGroupFormat m_format_options; +  OptionGroupReadMemory m_memory_options; +  OptionGroupOutputFile m_outfile_options; +  OptionGroupValueObjectDisplay m_varobj_options; +  lldb::addr_t m_next_addr; +  lldb::addr_t m_prev_byte_size; +  OptionGroupFormat m_prev_format_options; +  OptionGroupReadMemory m_prev_memory_options; +  OptionGroupOutputFile m_prev_outfile_options; +  OptionGroupValueObjectDisplay m_prev_varobj_options; +  CompilerType m_prev_compiler_type; +}; + +#define LLDB_OPTIONS_memory_find +#include "CommandOptions.inc" + +// Find the specified data in memory +class CommandObjectMemoryFind : public CommandObjectParsed { +public: +  class OptionGroupFindMemory : public OptionGroup { +  public: +    OptionGroupFindMemory() : OptionGroup(), m_count(1), m_offset(0) {} + +    ~OptionGroupFindMemory() override = default; + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_memory_find_options); +    } + +    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, +                          ExecutionContext *execution_context) override { +      Status error; +      const int short_option = g_memory_find_options[option_idx].short_option; + +      switch (short_option) { +      case 'e': +        m_expr.SetValueFromString(option_value); +        break; + +      case 's': +        m_string.SetValueFromString(option_value); +        break; + +      case 'c': +        if (m_count.SetValueFromString(option_value).Fail()) +          error.SetErrorString("unrecognized value for count"); +        break; + +      case 'o': +        if (m_offset.SetValueFromString(option_value).Fail()) +          error.SetErrorString("unrecognized value for dump-offset"); +        break; + +      default: +        llvm_unreachable("Unimplemented option"); +      } +      return error; +    } + +    void OptionParsingStarting(ExecutionContext *execution_context) override { +      m_expr.Clear(); +      m_string.Clear(); +      m_count.Clear(); +    } + +    OptionValueString m_expr; +    OptionValueString m_string; +    OptionValueUInt64 m_count; +    OptionValueUInt64 m_offset; +  }; + +  CommandObjectMemoryFind(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "memory find", +            "Find a value in the memory of the current target process.", +            nullptr, eCommandRequiresProcess | eCommandProcessMustBeLaunched), +        m_option_group(), m_memory_options() { +    CommandArgumentEntry arg1; +    CommandArgumentEntry arg2; +    CommandArgumentData addr_arg; +    CommandArgumentData value_arg; + +    // Define the first (and only) variant of this arg. +    addr_arg.arg_type = eArgTypeAddressOrExpression; +    addr_arg.arg_repetition = eArgRepeatPlain; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg1.push_back(addr_arg); + +    // Define the first (and only) variant of this arg. +    value_arg.arg_type = eArgTypeAddressOrExpression; +    value_arg.arg_repetition = eArgRepeatPlain; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg2.push_back(value_arg); + +    // Push the data for the first argument into the m_arguments vector. +    m_arguments.push_back(arg1); +    m_arguments.push_back(arg2); + +    m_option_group.Append(&m_memory_options); +    m_option_group.Finalize(); +  } + +  ~CommandObjectMemoryFind() override = default; + +  Options *GetOptions() override { return &m_option_group; } + +protected: +  class ProcessMemoryIterator { +  public: +    ProcessMemoryIterator(ProcessSP process_sp, lldb::addr_t base) +        : m_process_sp(process_sp), m_base_addr(base), m_is_valid(true) { +      lldbassert(process_sp.get() != nullptr); +    } + +    bool IsValid() { return m_is_valid; } + +    uint8_t operator[](lldb::addr_t offset) { +      if (!IsValid()) +        return 0; + +      uint8_t retval = 0; +      Status error; +      if (0 == +          m_process_sp->ReadMemory(m_base_addr + offset, &retval, 1, error)) { +        m_is_valid = false; +        return 0; +      } + +      return retval; +    } + +  private: +    ProcessSP m_process_sp; +    lldb::addr_t m_base_addr; +    bool m_is_valid; +  }; +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    // No need to check "process" for validity as eCommandRequiresProcess +    // ensures it is valid +    Process *process = m_exe_ctx.GetProcessPtr(); + +    const size_t argc = command.GetArgumentCount(); + +    if (argc != 2) { +      result.AppendError("two addresses needed for memory find"); +      return false; +    } + +    Status error; +    lldb::addr_t low_addr = OptionArgParser::ToAddress( +        &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); +    if (low_addr == LLDB_INVALID_ADDRESS || error.Fail()) { +      result.AppendError("invalid low address"); +      return false; +    } +    lldb::addr_t high_addr = OptionArgParser::ToAddress( +        &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, &error); +    if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) { +      result.AppendError("invalid high address"); +      return false; +    } + +    if (high_addr <= low_addr) { +      result.AppendError( +          "starting address must be smaller than ending address"); +      return false; +    } + +    lldb::addr_t found_location = LLDB_INVALID_ADDRESS; + +    DataBufferHeap buffer; + +    if (m_memory_options.m_string.OptionWasSet()) +      buffer.CopyData(m_memory_options.m_string.GetStringValue()); +    else if (m_memory_options.m_expr.OptionWasSet()) { +      StackFrame *frame = m_exe_ctx.GetFramePtr(); +      ValueObjectSP result_sp; +      if ((eExpressionCompleted == +           process->GetTarget().EvaluateExpression( +               m_memory_options.m_expr.GetStringValue(), frame, result_sp)) && +          result_sp) { +        uint64_t value = result_sp->GetValueAsUnsigned(0); +        llvm::Optional<uint64_t> size = +            result_sp->GetCompilerType().GetByteSize(nullptr); +        if (!size) +          return false; +        switch (*size) { +        case 1: { +          uint8_t byte = (uint8_t)value; +          buffer.CopyData(&byte, 1); +        } break; +        case 2: { +          uint16_t word = (uint16_t)value; +          buffer.CopyData(&word, 2); +        } break; +        case 4: { +          uint32_t lword = (uint32_t)value; +          buffer.CopyData(&lword, 4); +        } break; +        case 8: { +          buffer.CopyData(&value, 8); +        } break; +        case 3: +        case 5: +        case 6: +        case 7: +          result.AppendError("unknown type. pass a string instead"); +          return false; +        default: +          result.AppendError( +              "result size larger than 8 bytes. pass a string instead"); +          return false; +        } +      } else { +        result.AppendError( +            "expression evaluation failed. pass a string instead"); +        return false; +      } +    } else { +      result.AppendError( +          "please pass either a block of text, or an expression to evaluate."); +      return false; +    } + +    size_t count = m_memory_options.m_count.GetCurrentValue(); +    found_location = low_addr; +    bool ever_found = false; +    while (count) { +      found_location = FastSearch(found_location, high_addr, buffer.GetBytes(), +                                  buffer.GetByteSize()); +      if (found_location == LLDB_INVALID_ADDRESS) { +        if (!ever_found) { +          result.AppendMessage("data not found within the range.\n"); +          result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); +        } else +          result.AppendMessage("no more matches within the range.\n"); +        break; +      } +      result.AppendMessageWithFormat("data found at location: 0x%" PRIx64 "\n", +                                     found_location); + +      DataBufferHeap dumpbuffer(32, 0); +      process->ReadMemory( +          found_location + m_memory_options.m_offset.GetCurrentValue(), +          dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), error); +      if (!error.Fail()) { +        DataExtractor data(dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), +                           process->GetByteOrder(), +                           process->GetAddressByteSize()); +        DumpDataExtractor( +            data, &result.GetOutputStream(), 0, lldb::eFormatBytesWithASCII, 1, +            dumpbuffer.GetByteSize(), 16, +            found_location + m_memory_options.m_offset.GetCurrentValue(), 0, 0); +        result.GetOutputStream().EOL(); +      } + +      --count; +      found_location++; +      ever_found = true; +    } + +    result.SetStatus(lldb::eReturnStatusSuccessFinishResult); +    return true; +  } + +  lldb::addr_t FastSearch(lldb::addr_t low, lldb::addr_t high, uint8_t *buffer, +                          size_t buffer_size) { +    const size_t region_size = high - low; + +    if (region_size < buffer_size) +      return LLDB_INVALID_ADDRESS; + +    std::vector<size_t> bad_char_heuristic(256, buffer_size); +    ProcessSP process_sp = m_exe_ctx.GetProcessSP(); +    ProcessMemoryIterator iterator(process_sp, low); + +    for (size_t idx = 0; idx < buffer_size - 1; idx++) { +      decltype(bad_char_heuristic)::size_type bcu_idx = buffer[idx]; +      bad_char_heuristic[bcu_idx] = buffer_size - idx - 1; +    } +    for (size_t s = 0; s <= (region_size - buffer_size);) { +      int64_t j = buffer_size - 1; +      while (j >= 0 && buffer[j] == iterator[s + j]) +        j--; +      if (j < 0) +        return low + s; +      else +        s += bad_char_heuristic[iterator[s + buffer_size - 1]]; +    } + +    return LLDB_INVALID_ADDRESS; +  } + +  OptionGroupOptions m_option_group; +  OptionGroupFindMemory m_memory_options; +}; + +#define LLDB_OPTIONS_memory_write +#include "CommandOptions.inc" + +// Write memory to the inferior process +class CommandObjectMemoryWrite : public CommandObjectParsed { +public: +  class OptionGroupWriteMemory : public OptionGroup { +  public: +    OptionGroupWriteMemory() : OptionGroup() {} + +    ~OptionGroupWriteMemory() override = default; + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_memory_write_options); +    } + +    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, +                          ExecutionContext *execution_context) override { +      Status error; +      const int short_option = g_memory_write_options[option_idx].short_option; + +      switch (short_option) { +      case 'i': +        m_infile.SetFile(option_value, FileSpec::Style::native); +        FileSystem::Instance().Resolve(m_infile); +        if (!FileSystem::Instance().Exists(m_infile)) { +          m_infile.Clear(); +          error.SetErrorStringWithFormat("input file does not exist: '%s'", +                                         option_value.str().c_str()); +        } +        break; + +      case 'o': { +        if (option_value.getAsInteger(0, m_infile_offset)) { +          m_infile_offset = 0; +          error.SetErrorStringWithFormat("invalid offset string '%s'", +                                         option_value.str().c_str()); +        } +      } break; + +      default: +        llvm_unreachable("Unimplemented option"); +      } +      return error; +    } + +    void OptionParsingStarting(ExecutionContext *execution_context) override { +      m_infile.Clear(); +      m_infile_offset = 0; +    } + +    FileSpec m_infile; +    off_t m_infile_offset; +  }; + +  CommandObjectMemoryWrite(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "memory write", +            "Write to the memory of the current target process.", nullptr, +            eCommandRequiresProcess | eCommandProcessMustBeLaunched), +        m_option_group(), m_format_options(eFormatBytes, 1, UINT64_MAX), +        m_memory_options() { +    CommandArgumentEntry arg1; +    CommandArgumentEntry arg2; +    CommandArgumentData addr_arg; +    CommandArgumentData value_arg; + +    // Define the first (and only) variant of this arg. +    addr_arg.arg_type = eArgTypeAddress; +    addr_arg.arg_repetition = eArgRepeatPlain; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg1.push_back(addr_arg); + +    // Define the first (and only) variant of this arg. +    value_arg.arg_type = eArgTypeValue; +    value_arg.arg_repetition = eArgRepeatPlus; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg2.push_back(value_arg); + +    // Push the data for the first argument into the m_arguments vector. +    m_arguments.push_back(arg1); +    m_arguments.push_back(arg2); + +    m_option_group.Append(&m_format_options, +                          OptionGroupFormat::OPTION_GROUP_FORMAT, +                          LLDB_OPT_SET_1); +    m_option_group.Append(&m_format_options, +                          OptionGroupFormat::OPTION_GROUP_SIZE, +                          LLDB_OPT_SET_1 | LLDB_OPT_SET_2); +    m_option_group.Append(&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2); +    m_option_group.Finalize(); +  } + +  ~CommandObjectMemoryWrite() override = default; + +  Options *GetOptions() override { return &m_option_group; } + +  bool UIntValueIsValidForSize(uint64_t uval64, size_t total_byte_size) { +    if (total_byte_size > 8) +      return false; + +    if (total_byte_size == 8) +      return true; + +    const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1; +    return uval64 <= max; +  } + +  bool SIntValueIsValidForSize(int64_t sval64, size_t total_byte_size) { +    if (total_byte_size > 8) +      return false; + +    if (total_byte_size == 8) +      return true; + +    const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1; +    const int64_t min = ~(max); +    return min <= sval64 && sval64 <= max; +  } + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    // No need to check "process" for validity as eCommandRequiresProcess +    // ensures it is valid +    Process *process = m_exe_ctx.GetProcessPtr(); + +    const size_t argc = command.GetArgumentCount(); + +    if (m_memory_options.m_infile) { +      if (argc < 1) { +        result.AppendErrorWithFormat( +            "%s takes a destination address when writing file contents.\n", +            m_cmd_name.c_str()); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +    } else if (argc < 2) { +      result.AppendErrorWithFormat( +          "%s takes a destination address and at least one value.\n", +          m_cmd_name.c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    StreamString buffer( +        Stream::eBinary, +        process->GetTarget().GetArchitecture().GetAddressByteSize(), +        process->GetTarget().GetArchitecture().GetByteOrder()); + +    OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue(); +    size_t item_byte_size = byte_size_value.GetCurrentValue(); + +    Status error; +    lldb::addr_t addr = OptionArgParser::ToAddress( +        &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); + +    if (addr == LLDB_INVALID_ADDRESS) { +      result.AppendError("invalid address expression\n"); +      result.AppendError(error.AsCString()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    if (m_memory_options.m_infile) { +      size_t length = SIZE_MAX; +      if (item_byte_size > 1) +        length = item_byte_size; +      auto data_sp = FileSystem::Instance().CreateDataBuffer( +          m_memory_options.m_infile.GetPath(), length, +          m_memory_options.m_infile_offset); +      if (data_sp) { +        length = data_sp->GetByteSize(); +        if (length > 0) { +          Status error; +          size_t bytes_written = +              process->WriteMemory(addr, data_sp->GetBytes(), length, error); + +          if (bytes_written == length) { +            // All bytes written +            result.GetOutputStream().Printf( +                "%" PRIu64 " bytes were written to 0x%" PRIx64 "\n", +                (uint64_t)bytes_written, addr); +            result.SetStatus(eReturnStatusSuccessFinishResult); +          } else if (bytes_written > 0) { +            // Some byte written +            result.GetOutputStream().Printf( +                "%" PRIu64 " bytes of %" PRIu64 +                " requested were written to 0x%" PRIx64 "\n", +                (uint64_t)bytes_written, (uint64_t)length, addr); +            result.SetStatus(eReturnStatusSuccessFinishResult); +          } else { +            result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 +                                         " failed: %s.\n", +                                         addr, error.AsCString()); +            result.SetStatus(eReturnStatusFailed); +          } +        } +      } else { +        result.AppendErrorWithFormat("Unable to read contents of file.\n"); +        result.SetStatus(eReturnStatusFailed); +      } +      return result.Succeeded(); +    } else if (item_byte_size == 0) { +      if (m_format_options.GetFormat() == eFormatPointer) +        item_byte_size = buffer.GetAddressByteSize(); +      else +        item_byte_size = 1; +    } + +    command.Shift(); // shift off the address argument +    uint64_t uval64; +    int64_t sval64; +    bool success = false; +    for (auto &entry : command) { +      switch (m_format_options.GetFormat()) { +      case kNumFormats: +      case eFormatFloat: // TODO: add support for floats soon +      case eFormatCharPrintable: +      case eFormatBytesWithASCII: +      case eFormatComplex: +      case eFormatEnum: +      case eFormatUnicode8: +      case eFormatUnicode16: +      case eFormatUnicode32: +      case eFormatVectorOfChar: +      case eFormatVectorOfSInt8: +      case eFormatVectorOfUInt8: +      case eFormatVectorOfSInt16: +      case eFormatVectorOfUInt16: +      case eFormatVectorOfSInt32: +      case eFormatVectorOfUInt32: +      case eFormatVectorOfSInt64: +      case eFormatVectorOfUInt64: +      case eFormatVectorOfFloat16: +      case eFormatVectorOfFloat32: +      case eFormatVectorOfFloat64: +      case eFormatVectorOfUInt128: +      case eFormatOSType: +      case eFormatComplexInteger: +      case eFormatAddressInfo: +      case eFormatHexFloat: +      case eFormatInstruction: +      case eFormatVoid: +        result.AppendError("unsupported format for writing memory"); +        result.SetStatus(eReturnStatusFailed); +        return false; + +      case eFormatDefault: +      case eFormatBytes: +      case eFormatHex: +      case eFormatHexUppercase: +      case eFormatPointer: +      { +        // Decode hex bytes +        // Be careful, getAsInteger with a radix of 16 rejects "0xab" so we +        // have to special case that: +        bool success = false; +        if (entry.ref().startswith("0x")) +          success = !entry.ref().getAsInteger(0, uval64); +        if (!success) +          success = !entry.ref().getAsInteger(16, uval64); +        if (!success) { +          result.AppendErrorWithFormat( +              "'%s' is not a valid hex string value.\n", entry.c_str()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) { +          result.AppendErrorWithFormat("Value 0x%" PRIx64 +                                       " is too large to fit in a %" PRIu64 +                                       " byte unsigned integer value.\n", +                                       uval64, (uint64_t)item_byte_size); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +        buffer.PutMaxHex64(uval64, item_byte_size); +        break; +      } +      case eFormatBoolean: +        uval64 = OptionArgParser::ToBoolean(entry.ref(), false, &success); +        if (!success) { +          result.AppendErrorWithFormat( +              "'%s' is not a valid boolean string value.\n", entry.c_str()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +        buffer.PutMaxHex64(uval64, item_byte_size); +        break; + +      case eFormatBinary: +        if (entry.ref().getAsInteger(2, uval64)) { +          result.AppendErrorWithFormat( +              "'%s' is not a valid binary string value.\n", entry.c_str()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) { +          result.AppendErrorWithFormat("Value 0x%" PRIx64 +                                       " is too large to fit in a %" PRIu64 +                                       " byte unsigned integer value.\n", +                                       uval64, (uint64_t)item_byte_size); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +        buffer.PutMaxHex64(uval64, item_byte_size); +        break; + +      case eFormatCharArray: +      case eFormatChar: +      case eFormatCString: { +        if (entry.ref().empty()) +          break; + +        size_t len = entry.ref().size(); +        // Include the NULL for C strings... +        if (m_format_options.GetFormat() == eFormatCString) +          ++len; +        Status error; +        if (process->WriteMemory(addr, entry.c_str(), len, error) == len) { +          addr += len; +        } else { +          result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 +                                       " failed: %s.\n", +                                       addr, error.AsCString()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +        break; +      } +      case eFormatDecimal: +        if (entry.ref().getAsInteger(0, sval64)) { +          result.AppendErrorWithFormat( +              "'%s' is not a valid signed decimal value.\n", entry.c_str()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } else if (!SIntValueIsValidForSize(sval64, item_byte_size)) { +          result.AppendErrorWithFormat( +              "Value %" PRIi64 " is too large or small to fit in a %" PRIu64 +              " byte signed integer value.\n", +              sval64, (uint64_t)item_byte_size); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +        buffer.PutMaxHex64(sval64, item_byte_size); +        break; + +      case eFormatUnsigned: + +        if (!entry.ref().getAsInteger(0, uval64)) { +          result.AppendErrorWithFormat( +              "'%s' is not a valid unsigned decimal string value.\n", +              entry.c_str()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) { +          result.AppendErrorWithFormat("Value %" PRIu64 +                                       " is too large to fit in a %" PRIu64 +                                       " byte unsigned integer value.\n", +                                       uval64, (uint64_t)item_byte_size); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +        buffer.PutMaxHex64(uval64, item_byte_size); +        break; + +      case eFormatOctal: +        if (entry.ref().getAsInteger(8, uval64)) { +          result.AppendErrorWithFormat( +              "'%s' is not a valid octal string value.\n", entry.c_str()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) { +          result.AppendErrorWithFormat("Value %" PRIo64 +                                       " is too large to fit in a %" PRIu64 +                                       " byte unsigned integer value.\n", +                                       uval64, (uint64_t)item_byte_size); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +        buffer.PutMaxHex64(uval64, item_byte_size); +        break; +      } +    } + +    if (!buffer.GetString().empty()) { +      Status error; +      if (process->WriteMemory(addr, buffer.GetString().data(), +                               buffer.GetString().size(), +                               error) == buffer.GetString().size()) +        return true; +      else { +        result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 +                                     " failed: %s.\n", +                                     addr, error.AsCString()); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +    } +    return true; +  } + +  OptionGroupOptions m_option_group; +  OptionGroupFormat m_format_options; +  OptionGroupWriteMemory m_memory_options; +}; + +// Get malloc/free history of a memory address. +class CommandObjectMemoryHistory : public CommandObjectParsed { +public: +  CommandObjectMemoryHistory(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "memory history", "Print recorded stack traces for " +                                           "allocation/deallocation events " +                                           "associated with an address.", +            nullptr, +            eCommandRequiresTarget | eCommandRequiresProcess | +                eCommandProcessMustBePaused | eCommandProcessMustBeLaunched) { +    CommandArgumentEntry arg1; +    CommandArgumentData addr_arg; + +    // Define the first (and only) variant of this arg. +    addr_arg.arg_type = eArgTypeAddress; +    addr_arg.arg_repetition = eArgRepeatPlain; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg1.push_back(addr_arg); + +    // Push the data for the first argument into the m_arguments vector. +    m_arguments.push_back(arg1); +  } + +  ~CommandObjectMemoryHistory() override = default; + +  const char *GetRepeatCommand(Args ¤t_command_args, +                               uint32_t index) override { +    return m_cmd_name.c_str(); +  } + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    const size_t argc = command.GetArgumentCount(); + +    if (argc == 0 || argc > 1) { +      result.AppendErrorWithFormat("%s takes an address expression", +                                   m_cmd_name.c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    Status error; +    lldb::addr_t addr = OptionArgParser::ToAddress( +        &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); + +    if (addr == LLDB_INVALID_ADDRESS) { +      result.AppendError("invalid address expression"); +      result.AppendError(error.AsCString()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    Stream *output_stream = &result.GetOutputStream(); + +    const ProcessSP &process_sp = m_exe_ctx.GetProcessSP(); +    const MemoryHistorySP &memory_history = +        MemoryHistory::FindPlugin(process_sp); + +    if (!memory_history) { +      result.AppendError("no available memory history provider"); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    HistoryThreads thread_list = memory_history->GetHistoryThreads(addr); + +    const bool stop_format = false; +    for (auto thread : thread_list) { +      thread->GetStatus(*output_stream, 0, UINT32_MAX, 0, stop_format); +    } + +    result.SetStatus(eReturnStatusSuccessFinishResult); + +    return true; +  } +}; + +// CommandObjectMemoryRegion +#pragma mark CommandObjectMemoryRegion + +class CommandObjectMemoryRegion : public CommandObjectParsed { +public: +  CommandObjectMemoryRegion(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "memory region", +                            "Get information on the memory region containing " +                            "an address in the current target process.", +                            "memory region ADDR", +                            eCommandRequiresProcess | eCommandTryTargetAPILock | +                                eCommandProcessMustBeLaunched), +        m_prev_end_addr(LLDB_INVALID_ADDRESS) {} + +  ~CommandObjectMemoryRegion() override = default; + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    ProcessSP process_sp = m_exe_ctx.GetProcessSP(); +    if (process_sp) { +      Status error; +      lldb::addr_t load_addr = m_prev_end_addr; +      m_prev_end_addr = LLDB_INVALID_ADDRESS; + +      const size_t argc = command.GetArgumentCount(); +      if (argc > 1 || (argc == 0 && load_addr == LLDB_INVALID_ADDRESS)) { +        result.AppendErrorWithFormat("'%s' takes one argument:\nUsage: %s\n", +                                     m_cmd_name.c_str(), m_cmd_syntax.c_str()); +        result.SetStatus(eReturnStatusFailed); +      } else { +        if (command.GetArgumentCount() == 1) { +          auto load_addr_str = command[0].ref(); +          load_addr = OptionArgParser::ToAddress(&m_exe_ctx, load_addr_str, +                                                 LLDB_INVALID_ADDRESS, &error); +          if (error.Fail() || load_addr == LLDB_INVALID_ADDRESS) { +            result.AppendErrorWithFormat( +                "invalid address argument \"%s\": %s\n", command[0].c_str(), +                error.AsCString()); +            result.SetStatus(eReturnStatusFailed); +          } +        } + +        lldb_private::MemoryRegionInfo range_info; +        error = process_sp->GetMemoryRegionInfo(load_addr, range_info); +        if (error.Success()) { +          lldb_private::Address addr; +          ConstString name = range_info.GetName(); +          ConstString section_name; +          if (process_sp->GetTarget().ResolveLoadAddress(load_addr, addr)) { +            SectionSP section_sp(addr.GetSection()); +            if (section_sp) { +              // Got the top most section, not the deepest section +              while (section_sp->GetParent()) +                section_sp = section_sp->GetParent(); +              section_name = section_sp->GetName(); +            } +          } +          result.AppendMessageWithFormat( +              "[0x%16.16" PRIx64 "-0x%16.16" PRIx64 ") %c%c%c%s%s%s%s\n", +              range_info.GetRange().GetRangeBase(), +              range_info.GetRange().GetRangeEnd(), +              range_info.GetReadable() ? 'r' : '-', +              range_info.GetWritable() ? 'w' : '-', +              range_info.GetExecutable() ? 'x' : '-', +              name ? " " : "", name.AsCString(""), +              section_name ? " " : "", section_name.AsCString("")); +          m_prev_end_addr = range_info.GetRange().GetRangeEnd(); +          result.SetStatus(eReturnStatusSuccessFinishResult); +        } else { +          result.SetStatus(eReturnStatusFailed); +          result.AppendErrorWithFormat("%s\n", error.AsCString()); +        } +      } +    } else { +      m_prev_end_addr = LLDB_INVALID_ADDRESS; +      result.AppendError("invalid process"); +      result.SetStatus(eReturnStatusFailed); +    } +    return result.Succeeded(); +  } + +  const char *GetRepeatCommand(Args ¤t_command_args, +                               uint32_t index) override { +    // If we repeat this command, repeat it without any arguments so we can +    // show the next memory range +    return m_cmd_name.c_str(); +  } + +  lldb::addr_t m_prev_end_addr; +}; + +// CommandObjectMemory + +CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter) +    : CommandObjectMultiword( +          interpreter, "memory", +          "Commands for operating on memory in the current target process.", +          "memory <subcommand> [<subcommand-options>]") { +  LoadSubCommand("find", +                 CommandObjectSP(new CommandObjectMemoryFind(interpreter))); +  LoadSubCommand("read", +                 CommandObjectSP(new CommandObjectMemoryRead(interpreter))); +  LoadSubCommand("write", +                 CommandObjectSP(new CommandObjectMemoryWrite(interpreter))); +  LoadSubCommand("history", +                 CommandObjectSP(new CommandObjectMemoryHistory(interpreter))); +  LoadSubCommand("region", +                 CommandObjectSP(new CommandObjectMemoryRegion(interpreter))); +} + +CommandObjectMemory::~CommandObjectMemory() = default;  | 
