diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h index 37dacca6ff8134192c638087912483a62aa352c7..20629985196afebc67f150ae00cb7936509c1d56 100644 --- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -98,6 +98,9 @@ public: Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read); + Status ShowMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read); + virtual Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len, std::vector &tags); diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 505e211e09b630fceca4c099ec99b2b7cb35abbf..4c9270984b9bd0278f7031cb9ad7325e48afa64e 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -1447,6 +1447,8 @@ public: /// returned in the case of an error. virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error); + virtual size_t ShowMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error); /// Read of memory from a process. /// @@ -2601,6 +2603,10 @@ protected: /// Zero is returned in the case of an error. virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error) = 0; + virtual size_t DoShowMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Status &error) { + return 0; + } /// DoGetMemoryRegionInfo is called by GetMemoryRegionInfo after it has /// removed non address bits from load_addr. Override this method in diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 3af8c71dd977057be8f34b3b5d033f16e4d4cfdc..78bd5351e779a5a1d4c276eb1c2fadb51291d7d9 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -1037,6 +1037,9 @@ public: size_t ReadMemory(const Address &addr, void *dst, size_t dst_len, Status &error, bool force_live_memory = false, lldb::addr_t *load_addr_ptr = nullptr); + size_t ShowMemory(const Address &addr, void *dst, size_t dst_len, + Status &error, bool force_live_memory = false, + lldb::addr_t *load_addr_ptr = nullptr); size_t ReadCStringFromMemory(const Address &addr, std::string &out_str, Status &error, bool force_live_memory = false); diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h index d869950ab6dd1788a7e24f71f3365401451c9916..4fdc01980d65ae5756251931abe89af8b0ad9902 100644 --- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -158,6 +158,7 @@ public: eServerPacketType_T, eServerPacketType_x, eServerPacketType_X, + eServerPacketType_y, eServerPacketType_Z, eServerPacketType_z, diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index 5051f9aeec851cd08567a44a23c567c796156280..9abc0f956ad62e308f176e79afcea39137379378 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -279,7 +279,615 @@ public: OptionValueUInt64 m_offset; OptionValueLanguage m_language_for_type; }; +// Read memory from the inferior process +class CommandObjectMemoryShow : public CommandObjectParsed { +public: + CommandObjectMemoryShow(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "memory show", + "show the memory of the current target process.", nullptr, + eCommandRequiresTarget | eCommandProcessMustBePaused), + m_format_options(eFormatBytesWithASCII, 1, 8), + m_memory_tag_options(/*note_binary=*/true), + m_prev_format_options(eFormatBytesWithASCII, 1, 8) { + 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.Append(&m_memory_tag_options, LLDB_OPT_SET_ALL, + LLDB_OPT_SET_ALL); + m_option_group.Finalize(); + } + + ~CommandObjectMemoryShow() override = default; + + Options *GetOptions() override { return &m_option_group; } + + llvm::Optional GetRepeatCommand(Args ¤t_command_args, + uint32_t index) override { + return m_cmd_name; + } + +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.AppendWarning("Expressions should be quoted if they contain " + "spaces or other special characters."); + 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); + return false; + } + break; + + default: + done = true; + break; + } + } + } + + llvm::DenseSet 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 languages_to_check; + if (language_for_type != eLanguageTypeUnknown) { + languages_to_check.insert(language_for_type); + } else { + languages_to_check = Language::GetSupportedLanguages(); + } + + std::set user_defined_types; + for (auto lang : languages_to_check) { + if (auto *persistent_vars = + target->GetPersistentExpressionStateForLanguage(lang)) { + if (llvm::Optional 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()); + 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); + 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"); + return false; + } + --pointer_count; + } + + llvm::Optional size = compiler_type.GetByteSize(nullptr); + if (!size) { + result.AppendErrorWithFormat( + "unable to get the byte size of the type '%s'\n", + view_as_type_cstr); + 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()); + 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_memory_tag_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; + m_memory_tag_options = m_prev_memory_tag_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()); + return false; + } + + ABISP abi; + if (Process *proc = m_exe_ctx.GetProcessPtr()) + abi = proc->GetABI(); + + if (abi) + addr = abi->FixDataAddress(addr); + + 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 && abi) + end_addr = abi->FixDataAddress(end_addr); + + if (end_addr == LLDB_INVALID_ADDRESS) { + result.AppendError("invalid end address expression."); + result.AppendError(error.AsCString()); + return false; + } else if (end_addr <= addr) { + result.AppendErrorWithFormat( + "end address (0x%" PRIx64 + ") must be greater than the start address (0x%" PRIx64 ").\n", + end_addr, addr); + 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); + 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 show\' will not show 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; + } + + WritableDataBufferSP 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 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(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); + return false; + } + + Address address(addr, nullptr); + bytes_read = target->ShowMemory(address, data_sp->GetBytes(), + data_sp->GetByteSize(), error, true); + 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); + } + 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( + (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)); + 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); + 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(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_memory_tag_options = m_memory_tag_options; + m_prev_compiler_type = compiler_type; + + std::unique_ptr 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) { + + File::OpenOptions open_options = + File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate; + const bool append = m_outfile_options.GetAppend().GetCurrentValue(); + open_options |= + append ? File::eOpenOptionAppend : File::eOpenOptionTruncate; + + auto outfile = FileSystem::Instance().Open(outfile_spec, open_options); + + if (outfile) { + auto outfile_stream_up = + std::make_unique(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()); + 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())); + 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()); + 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); + 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_memory_tag_options.GetShowTags().GetCurrentValue()); + 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; + OptionGroupMemoryTag m_memory_tag_options; + lldb::addr_t m_next_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t m_prev_byte_size = 0; + OptionGroupFormat m_prev_format_options; + OptionGroupReadMemory m_prev_memory_options; + OptionGroupOutputFile m_prev_outfile_options; + OptionGroupValueObjectDisplay m_prev_varobj_options; + OptionGroupMemoryTag m_prev_memory_tag_options; + CompilerType m_prev_compiler_type; +}; // Read memory from the inferior process class CommandObjectMemoryRead : public CommandObjectParsed { public: @@ -1878,6 +2486,8 @@ CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter) CommandObjectSP(new CommandObjectMemoryFind(interpreter))); LoadSubCommand("read", CommandObjectSP(new CommandObjectMemoryRead(interpreter))); + LoadSubCommand("show", + CommandObjectSP(new CommandObjectMemoryShow(interpreter))); LoadSubCommand("write", CommandObjectSP(new CommandObjectMemoryWrite(interpreter))); LoadSubCommand("history", diff --git a/lldb/source/Host/common/NativeProcessProtocol.cpp b/lldb/source/Host/common/NativeProcessProtocol.cpp index 69b04ee55ba297550c928e7f6546f72e89c42ac6..1138bb2fe6d37f2b000478ddae44bfaacd8c3c47 100644 --- a/lldb/source/Host/common/NativeProcessProtocol.cpp +++ b/lldb/source/Host/common/NativeProcessProtocol.cpp @@ -659,6 +659,15 @@ Status NativeProcessProtocol::ReadMemoryWithoutTrap(lldb::addr_t addr, return Status(); } +Status NativeProcessProtocol::ShowMemoryWithoutTrap(lldb::addr_t addr, + void *buf, size_t size, + size_t &bytes_read) { + Status error = ReadMemory(addr, buf, size, bytes_read); + if (error.Fail()) + return error; + return Status(); +} + llvm::Expected NativeProcessProtocol::ReadCStringFromMemory(lldb::addr_t addr, char *buffer, size_t max_size, diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index 635dbb14a0271692d21b7105152f23f3b965ab85..22f8ffd4c8dd6d95a8f44620964296be37705b94 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -172,6 +172,10 @@ struct ForkLaunchInfo { ec = llvm::sys::fs::is_directory(proc_fd_path, result); if (result) { std::vector files_to_close; + //get proc_fd_path for the process(/proc/pid/fd) + char path [100]; + lldb::pid_t pid=getpid(); + std::string dir_name= "/proc/"+std::to_string(pid)+"/fd"; // Directory iterator doesn't ensure any sequence. for (llvm::sys::fs::directory_iterator iter(proc_fd_path, ec), file_end; iter != file_end && !ec; iter.increment(ec)) { @@ -179,8 +183,17 @@ struct ForkLaunchInfo { // Don't close first three entries since they are stdin, stdout and // stderr. - if (fd > 2 && !info.has_action(fd) && fd != error_fd) + if (fd > 2 && !info.has_action(fd) && fd != error_fd){ + ssize_t len =readlink(iter->path().c_str(),path,sizeof(path)-1); + if (len!=-1){ + path[len]='\0'; + } + if (dir_name.compare(std::string(path))==0) + continue; + files_to_close.push_back(fd); + } + } for (int file_to_close : files_to_close) close(file_to_close); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 5804c13fe7b6447c52025bda2decc6b94954c36e..f2556b09392f9bd23b0fa78a1ce584298fa8fe8b 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -95,6 +95,10 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_m, &GDBRemoteCommunicationServerLLGS::Handle_memory_read); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_y, + &GDBRemoteCommunicationServerLLGS::Handle_memory_show); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M, &GDBRemoteCommunicationServerLLGS::Handle_M); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType__M, @@ -2550,6 +2554,87 @@ GDBRemoteCommunicationServerLLGS::Handle_memory_read( return SendPacketNoLock(response.GetString()); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_memory_show( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Parse out the memory address. + packet.SetFilePos(strlen("y")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short m packet"); + + // Read the address. Punting on validation. + // FIXME replace with Hex U64 read with no default value that fails on failed + // read. + const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); + + // Validate comma. + if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) + return SendIllFormedResponse(packet, "Comma sep missing in m packet"); + + // Get # bytes to read. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Length missing in m packet"); + + const uint64_t byte_count = packet.GetHexMaxU64(false, 0); + if (byte_count == 0) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s nothing to read: " + "zero-length packet", + __FUNCTION__); + return SendOKResponse(); + } + + // Allocate the response buffer. + std::string buf(byte_count, '\0'); + if (buf.empty()) + return SendErrorResponse(0x78); + + // Retrieve the process memory. + size_t bytes_read = 0; + Status error = m_current_process->ShowMemoryWithoutTrap( + read_addr, &buf[0], byte_count, bytes_read); + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " mem 0x%" PRIx64 ": failed to read. Error: %s", + __FUNCTION__, m_current_process->GetID(), read_addr, + error.AsCString()); + return SendErrorResponse(0x08); + } + + if (bytes_read == 0) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " mem 0x%" PRIx64 ": read 0 of %" PRIu64 " requested bytes", + __FUNCTION__, m_current_process->GetID(), read_addr, byte_count); + return SendErrorResponse(0x08); + } + + StreamGDBRemote response; + packet.SetFilePos(0); + char kind = packet.GetChar('?'); + if (kind == 'y') + response.PutEscapedBytes(buf.data(), byte_count); + else { + assert(kind == 'm'); + for (size_t i = 0; i < bytes_read; ++i) + response.PutHex8(buf[i]); + } + + return SendPacketNoLock(response.GetString()); +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle__M(StringExtractorGDBRemote &packet) { Log *log = GetLog(LLDBLog::Process); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h index 1165b60ac762b394e6376d753c8225612b702ccb..2dbbe772c54dfa205c95962f73a86ef5ce6d0bce 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -192,6 +192,8 @@ protected: // Handles $m and $x packets. PacketResult Handle_memory_read(StringExtractorGDBRemote &packet); + PacketResult Handle_memory_show(StringExtractorGDBRemote &packet); + PacketResult Handle_M(StringExtractorGDBRemote &packet); PacketResult Handle__M(StringExtractorGDBRemote &packet); PacketResult Handle__m(StringExtractorGDBRemote &packet); diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 7423f1b93a3b4b13e57e9ef124b25389d5a4921f..d347e13ab1aabb257483e7697595f3c11defc099 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -2581,6 +2581,66 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, return 0; } +size_t ProcessGDBRemote::DoShowMemory(addr_t addr, void *buf, size_t size, + Status &error) { + LLDB_MODULE_TIMER(LLDBPerformanceTagName::TAG_GDBREMOTE); // OHOS_LOCAL + GetMaxMemorySize(); + bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); + // M and m packets take 2 bytes for 1 byte of memory + size_t max_memory_size = + binary_memory_read ? m_max_memory_size : m_max_memory_size / 2; + if (size > max_memory_size) { + // Keep memory read sizes down to a sane limit. This function will be + // called multiple times in order to complete the task by + // lldb_private::Process so it is ok to do this. + size = max_memory_size; + } + + char packet[64]; + int packet_len; + packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64, + binary_memory_read ? 'y' : 'm', (uint64_t)addr, + (uint64_t)size); + assert(packet_len + 1 < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, + GetInterruptTimeout()) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsNormalResponse()) { + error.Clear(); + if (binary_memory_read) { + // The lower level GDBRemoteCommunication packet receive layer has + // already de-quoted any 0x7d character escaping that was present in + // the packet + + size_t data_received_size = response.GetBytesLeft(); + if (data_received_size > size) { + // Don't write past the end of BUF if the remote debug server gave us + // too much data for some reason. + data_received_size = size; + } + memcpy(buf, response.GetStringRef().data(), data_received_size); + return data_received_size; + } else { + return response.GetHexBytes( + llvm::MutableArrayRef((uint8_t *)buf, size), '\xdd'); + } + } else if (response.IsErrorResponse()) + error.SetErrorStringWithFormat("memory show failed for 0x%" PRIx64, addr); + else if (response.IsUnsupportedResponse()) + error.SetErrorStringWithFormat( + "GDB server does not support reading memory"); + else + error.SetErrorStringWithFormat( + "unexpected response to GDB server memory show packet '%s': '%s'", + packet, response.GetStringRef().data()); + } else { + error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); + } + return 0; +} + bool ProcessGDBRemote::SupportsMemoryTagging() { return m_gdb_comm.GetMemoryTaggingSupported(); } diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 50cef8e499dcc97e9bffd918808b024e0495cb04..3aff3073082861f27da5d39d6925f768d7d3a443 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -136,6 +136,9 @@ public: size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) override; + size_t DoShowMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + Status WriteObjectFile(std::vector entries) override; diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp index 64219e1a960b868d93cab051cb0351a5b08b5e8a..857ee89dddc3cff0e41f0737f84a45f445f14e66 100644 --- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp +++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -387,6 +387,19 @@ size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, return mem.size(); } +size_t ProcessMinidump::DoShowMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + + llvm::ArrayRef mem = m_minidump_parser->GetMemory(addr, size); + if (mem.empty()) { + error.SetErrorString("could not parse memory info"); + return 0; + } + + std::memcpy(buf, mem.data(), mem.size()); + return mem.size(); +} + ArchSpec ProcessMinidump::GetArchitecture() { if (!m_is_wow64) { return m_minidump_parser->GetArchitecture(); diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h index 5360269199cdd80aea83066ff978ef84618acac1..43a215bc56be06b4b1111fa9c33752c744702b08 100644 --- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h +++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h @@ -71,6 +71,8 @@ public: Status &error) override; size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override;\ + size_t DoShowMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) override; ArchSpec GetArchitecture(); diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp index cd2d4fe4fd234beea5762ebe06256396d6952b8c..0b5f0fbf84a7870d14979364de8290438eba3b6f 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp @@ -242,6 +242,28 @@ size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, return size; } +size_t ScriptedProcess::DoShowMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + if (!m_interpreter) + return ScriptedInterface::ErrorWithMessage( + LLVM_PRETTY_FUNCTION, "No interpreter.", error); + + lldb::DataExtractorSP data_extractor_sp = + GetInterface().ReadMemoryAtAddress(addr, size, error); + + if (!data_extractor_sp || !data_extractor_sp->GetByteSize() || error.Fail()) + return 0; + + offset_t bytes_copied = data_extractor_sp->CopyByteOrderedData( + 0, data_extractor_sp->GetByteSize(), buf, size, GetByteOrder()); + + if (!bytes_copied || bytes_copied == LLDB_INVALID_OFFSET) + return ScriptedInterface::ErrorWithMessage( + LLVM_PRETTY_FUNCTION, "Failed to copy read memory to buffer.", error); + + return size; +} + ArchSpec ScriptedProcess::GetArchitecture() { return GetTarget().GetArchitecture(); } diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h index 7edd95e230a163503419e299b917730c01cfdf4c..f0ec4d3ef0df01acde29170b30514fd05cf6618e 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h @@ -81,6 +81,8 @@ public: size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) override; + size_t DoShowMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; ArchSpec GetArchitecture(); diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index e1a60fba29b5401daad34f5984ea55406c3dcd14..96c0a2821cc8e3db6823e01e66d62618eb5f4407 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -1967,6 +1967,35 @@ size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) { } } +size_t Process::ShowMemory(addr_t addr, void *buf, size_t size, Status &error) { + if (ABISP abi_sp = GetABI()) + addr = abi_sp->FixAnyAddress(addr); + + LLDB_SCOPED_TIMER(); + LLDB_MODULE_TIMER(LLDBPerformanceTagName::TAG_PROCESS); + if (buf==nullptr||size==0){ + return 0; + } + size_t bytes_read = 0; + uint8_t *bytes = (uint8_t *)buf; + + while (bytes_read < size) { + const size_t curr_size = size - bytes_read; + const size_t curr_bytes_read = + DoShowMemory(addr + bytes_read, bytes + bytes_read, curr_size, error); + bytes_read += curr_bytes_read; + if (curr_bytes_read == curr_size || curr_bytes_read == 0) + break; + } + + if (bytes_read > 0) + RemoveBreakpointOpcodesFromBuffer(addr, bytes_read, (uint8_t *)buf); + return bytes_read; + + + +} + size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str, Status &error) { char buf[256]; diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 3d39fe8152a531db6e419f3d11e90ff071d22926..2f377b408f6c153ce5cce48e11904d9e3554a218 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -1867,6 +1867,127 @@ size_t Target::ReadMemory(const Address &addr, void *dst, size_t dst_len, return 0; } +size_t Target::ShowMemory(const Address &addr, void *dst, size_t dst_len, + Status &error, bool force_live_memory, + lldb::addr_t *load_addr_ptr) { + error.Clear(); + + Address fixed_addr = addr; + if (ProcessIsValid()) + if (const ABISP &abi = m_process_sp->GetABI()) + fixed_addr.SetLoadAddress(abi->FixAnyAddress(addr.GetLoadAddress(this)), + this); + + // if we end up reading this from process memory, we will fill this with the + // actual load address + if (load_addr_ptr) + *load_addr_ptr = LLDB_INVALID_ADDRESS; + + size_t bytes_read = 0; + + addr_t load_addr = LLDB_INVALID_ADDRESS; + addr_t file_addr = LLDB_INVALID_ADDRESS; + Address resolved_addr; + if (!fixed_addr.IsSectionOffset()) { + SectionLoadList §ion_load_list = GetSectionLoadList(); + if (section_load_list.IsEmpty()) { + // No sections are loaded, so we must assume we are not running yet and + // anything we are given is a file address. + file_addr = + fixed_addr.GetOffset(); // "fixed_addr" doesn't have a section, so + // its offset is the file address + m_images.ResolveFileAddress(file_addr, resolved_addr); + } else { + // We have at least one section loaded. This can be because we have + // manually loaded some sections with "target modules load ..." or + // because we have have a live process that has sections loaded through + // the dynamic loader + load_addr = + fixed_addr.GetOffset(); // "fixed_addr" doesn't have a section, so + // its offset is the load address + section_load_list.ResolveLoadAddress(load_addr, resolved_addr); + } + } + if (!resolved_addr.IsValid()) + resolved_addr = fixed_addr; + + // If we read from the file cache but can't get as many bytes as requested, + // we keep the result around in this buffer, in case this result is the + // best we can do. + std::unique_ptr file_cache_read_buffer; + size_t file_cache_bytes_read = 0; + + // Read from file cache if read-only section. + if (!force_live_memory && resolved_addr.IsSectionOffset()) { + SectionSP section_sp(resolved_addr.GetSection()); + if (section_sp) { + auto permissions = Flags(section_sp->GetPermissions()); + bool is_readonly = !permissions.Test(ePermissionsWritable) && + permissions.Test(ePermissionsReadable); + if (is_readonly) { + file_cache_bytes_read = + ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); + if (file_cache_bytes_read == dst_len) + return file_cache_bytes_read; + else if (file_cache_bytes_read > 0) { + file_cache_read_buffer = + std::make_unique(file_cache_bytes_read); + std::memcpy(file_cache_read_buffer.get(), dst, file_cache_bytes_read); + } + } + } + } + + if (ProcessIsValid()) { + if (load_addr == LLDB_INVALID_ADDRESS) + load_addr = resolved_addr.GetLoadAddress(this); + + if (load_addr == LLDB_INVALID_ADDRESS) { + ModuleSP addr_module_sp(resolved_addr.GetModule()); + if (addr_module_sp && addr_module_sp->GetFileSpec()) + error.SetErrorStringWithFormatv( + "{0:F}[{1:x+}] can't be resolved, {0:F} is not currently loaded", + addr_module_sp->GetFileSpec(), resolved_addr.GetFileAddress()); + else + error.SetErrorStringWithFormat("0x%" PRIx64 " can't be resolved", + resolved_addr.GetFileAddress()); + } else { + bytes_read = m_process_sp->ShowMemory(load_addr, dst, dst_len, error); + if (bytes_read != dst_len) { + if (error.Success()) { + if (bytes_read == 0) + error.SetErrorStringWithFormat( + "read memory from 0x%" PRIx64 " failed", load_addr); + else + error.SetErrorStringWithFormat( + "only %" PRIu64 " of %" PRIu64 + " bytes were read from memory at 0x%" PRIx64, + (uint64_t)bytes_read, (uint64_t)dst_len, load_addr); + } + } + if (bytes_read) { + if (load_addr_ptr) + *load_addr_ptr = load_addr; + return bytes_read; + } + } + } + + if (file_cache_read_buffer && file_cache_bytes_read > 0) { + // Reading from the process failed. If we've previously succeeded in reading + // something from the file cache, then copy that over and return that. + std::memcpy(dst, file_cache_read_buffer.get(), file_cache_bytes_read); + return file_cache_bytes_read; + } + + if (!file_cache_read_buffer && resolved_addr.IsSectionOffset()) { + // If we didn't already try and read from the object file cache, then try + // it after failing to read from the process. + return ReadMemoryFromFileCache(resolved_addr, dst, dst_len, error); + } + return 0; +} + size_t Target::ReadCStringFromMemory(const Address &addr, std::string &out_str, Status &error, bool force_live_memory) { char buf[256]; diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp index fc740615dd05a28c78f6f90d70ee16b60dac2688..1d8478b4dfb306d6a49236efe06f21ec8f735d6d 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -449,6 +449,8 @@ StringExtractorGDBRemote::GetServerPacketType() const { case 'x': return eServerPacketType_x; + case 'y': + return eServerPacketType_y; case 'X': return eServerPacketType_X;