15ffd83dbSDimitry Andric //===-- CommandObjectMemory.cpp -------------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "CommandObjectMemory.h" 10fe6060f1SDimitry Andric #include "CommandObjectMemoryTag.h" 110b57cec5SDimitry Andric #include "lldb/Core/DumpDataExtractor.h" 120b57cec5SDimitry Andric #include "lldb/Core/Section.h" 130b57cec5SDimitry Andric #include "lldb/Core/ValueObjectMemory.h" 140b57cec5SDimitry Andric #include "lldb/Expression/ExpressionVariable.h" 150b57cec5SDimitry Andric #include "lldb/Host/OptionParser.h" 16fcaf7f86SDimitry Andric #include "lldb/Interpreter/CommandOptionArgumentTable.h" 170b57cec5SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h" 180b57cec5SDimitry Andric #include "lldb/Interpreter/OptionArgParser.h" 190b57cec5SDimitry Andric #include "lldb/Interpreter/OptionGroupFormat.h" 2081ad6265SDimitry Andric #include "lldb/Interpreter/OptionGroupMemoryTag.h" 210b57cec5SDimitry Andric #include "lldb/Interpreter/OptionGroupOutputFile.h" 220b57cec5SDimitry Andric #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" 230b57cec5SDimitry Andric #include "lldb/Interpreter/OptionValueLanguage.h" 240b57cec5SDimitry Andric #include "lldb/Interpreter/OptionValueString.h" 250b57cec5SDimitry Andric #include "lldb/Interpreter/Options.h" 260b57cec5SDimitry Andric #include "lldb/Symbol/SymbolFile.h" 270b57cec5SDimitry Andric #include "lldb/Symbol/TypeList.h" 2804eeddc0SDimitry Andric #include "lldb/Target/ABI.h" 290b57cec5SDimitry Andric #include "lldb/Target/Language.h" 300b57cec5SDimitry Andric #include "lldb/Target/MemoryHistory.h" 310b57cec5SDimitry Andric #include "lldb/Target/MemoryRegionInfo.h" 320b57cec5SDimitry Andric #include "lldb/Target/Process.h" 330b57cec5SDimitry Andric #include "lldb/Target/StackFrame.h" 340b57cec5SDimitry Andric #include "lldb/Target/Target.h" 350b57cec5SDimitry Andric #include "lldb/Target/Thread.h" 360b57cec5SDimitry Andric #include "lldb/Utility/Args.h" 370b57cec5SDimitry Andric #include "lldb/Utility/DataBufferHeap.h" 380b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h" 39e8d8bef9SDimitry Andric #include "llvm/Support/MathExtras.h" 400b57cec5SDimitry Andric #include <cinttypes> 410b57cec5SDimitry Andric #include <memory> 42bdd1243dSDimitry Andric #include <optional> 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric using namespace lldb; 450b57cec5SDimitry Andric using namespace lldb_private; 460b57cec5SDimitry Andric 479dba64beSDimitry Andric #define LLDB_OPTIONS_memory_read 489dba64beSDimitry Andric #include "CommandOptions.inc" 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric class OptionGroupReadMemory : public OptionGroup { 510b57cec5SDimitry Andric public: 520b57cec5SDimitry Andric OptionGroupReadMemory() 5304eeddc0SDimitry Andric : m_num_per_line(1, 1), m_offset(0, 0), 54fe6060f1SDimitry Andric m_language_for_type(eLanguageTypeUnknown) {} 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric ~OptionGroupReadMemory() override = default; 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 59bdd1243dSDimitry Andric return llvm::ArrayRef(g_memory_read_options); 600b57cec5SDimitry Andric } 610b57cec5SDimitry Andric 620b57cec5SDimitry Andric Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, 630b57cec5SDimitry Andric ExecutionContext *execution_context) override { 640b57cec5SDimitry Andric Status error; 659dba64beSDimitry Andric const int short_option = g_memory_read_options[option_idx].short_option; 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric switch (short_option) { 680b57cec5SDimitry Andric case 'l': 690b57cec5SDimitry Andric error = m_num_per_line.SetValueFromString(option_value); 700b57cec5SDimitry Andric if (m_num_per_line.GetCurrentValue() == 0) 710b57cec5SDimitry Andric error.SetErrorStringWithFormat( 720b57cec5SDimitry Andric "invalid value for --num-per-line option '%s'", 730b57cec5SDimitry Andric option_value.str().c_str()); 740b57cec5SDimitry Andric break; 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric case 'b': 770b57cec5SDimitry Andric m_output_as_binary = true; 780b57cec5SDimitry Andric break; 790b57cec5SDimitry Andric 800b57cec5SDimitry Andric case 't': 810b57cec5SDimitry Andric error = m_view_as_type.SetValueFromString(option_value); 820b57cec5SDimitry Andric break; 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric case 'r': 850b57cec5SDimitry Andric m_force = true; 860b57cec5SDimitry Andric break; 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric case 'x': 890b57cec5SDimitry Andric error = m_language_for_type.SetValueFromString(option_value); 900b57cec5SDimitry Andric break; 910b57cec5SDimitry Andric 920b57cec5SDimitry Andric case 'E': 930b57cec5SDimitry Andric error = m_offset.SetValueFromString(option_value); 940b57cec5SDimitry Andric break; 950b57cec5SDimitry Andric 960b57cec5SDimitry Andric default: 979dba64beSDimitry Andric llvm_unreachable("Unimplemented option"); 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric return error; 1000b57cec5SDimitry Andric } 1010b57cec5SDimitry Andric 1020b57cec5SDimitry Andric void OptionParsingStarting(ExecutionContext *execution_context) override { 1030b57cec5SDimitry Andric m_num_per_line.Clear(); 1040b57cec5SDimitry Andric m_output_as_binary = false; 1050b57cec5SDimitry Andric m_view_as_type.Clear(); 1060b57cec5SDimitry Andric m_force = false; 1070b57cec5SDimitry Andric m_offset.Clear(); 1080b57cec5SDimitry Andric m_language_for_type.Clear(); 1090b57cec5SDimitry Andric } 1100b57cec5SDimitry Andric 1110b57cec5SDimitry Andric Status FinalizeSettings(Target *target, OptionGroupFormat &format_options) { 1120b57cec5SDimitry Andric Status error; 1130b57cec5SDimitry Andric OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue(); 1140b57cec5SDimitry Andric OptionValueUInt64 &count_value = format_options.GetCountValue(); 1150b57cec5SDimitry Andric const bool byte_size_option_set = byte_size_value.OptionWasSet(); 1160b57cec5SDimitry Andric const bool num_per_line_option_set = m_num_per_line.OptionWasSet(); 1170b57cec5SDimitry Andric const bool count_option_set = format_options.GetCountValue().OptionWasSet(); 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric switch (format_options.GetFormat()) { 1200b57cec5SDimitry Andric default: 1210b57cec5SDimitry Andric break; 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric case eFormatBoolean: 1240b57cec5SDimitry Andric if (!byte_size_option_set) 1250b57cec5SDimitry Andric byte_size_value = 1; 1260b57cec5SDimitry Andric if (!num_per_line_option_set) 1270b57cec5SDimitry Andric m_num_per_line = 1; 1280b57cec5SDimitry Andric if (!count_option_set) 1290b57cec5SDimitry Andric format_options.GetCountValue() = 8; 1300b57cec5SDimitry Andric break; 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric case eFormatCString: 1330b57cec5SDimitry Andric break; 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric case eFormatInstruction: 1360b57cec5SDimitry Andric if (count_option_set) 1370b57cec5SDimitry Andric byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize(); 1380b57cec5SDimitry Andric m_num_per_line = 1; 1390b57cec5SDimitry Andric break; 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric case eFormatAddressInfo: 1420b57cec5SDimitry Andric if (!byte_size_option_set) 1430b57cec5SDimitry Andric byte_size_value = target->GetArchitecture().GetAddressByteSize(); 1440b57cec5SDimitry Andric m_num_per_line = 1; 1450b57cec5SDimitry Andric if (!count_option_set) 1460b57cec5SDimitry Andric format_options.GetCountValue() = 8; 1470b57cec5SDimitry Andric break; 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric case eFormatPointer: 1500b57cec5SDimitry Andric byte_size_value = target->GetArchitecture().GetAddressByteSize(); 1510b57cec5SDimitry Andric if (!num_per_line_option_set) 1520b57cec5SDimitry Andric m_num_per_line = 4; 1530b57cec5SDimitry Andric if (!count_option_set) 1540b57cec5SDimitry Andric format_options.GetCountValue() = 8; 1550b57cec5SDimitry Andric break; 1560b57cec5SDimitry Andric 1570b57cec5SDimitry Andric case eFormatBinary: 1580b57cec5SDimitry Andric case eFormatFloat: 1590b57cec5SDimitry Andric case eFormatOctal: 1600b57cec5SDimitry Andric case eFormatDecimal: 1610b57cec5SDimitry Andric case eFormatEnum: 1629dba64beSDimitry Andric case eFormatUnicode8: 1630b57cec5SDimitry Andric case eFormatUnicode16: 1640b57cec5SDimitry Andric case eFormatUnicode32: 1650b57cec5SDimitry Andric case eFormatUnsigned: 1660b57cec5SDimitry Andric case eFormatHexFloat: 1670b57cec5SDimitry Andric if (!byte_size_option_set) 1680b57cec5SDimitry Andric byte_size_value = 4; 1690b57cec5SDimitry Andric if (!num_per_line_option_set) 1700b57cec5SDimitry Andric m_num_per_line = 1; 1710b57cec5SDimitry Andric if (!count_option_set) 1720b57cec5SDimitry Andric format_options.GetCountValue() = 8; 1730b57cec5SDimitry Andric break; 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric case eFormatBytes: 1760b57cec5SDimitry Andric case eFormatBytesWithASCII: 1770b57cec5SDimitry Andric if (byte_size_option_set) { 1780b57cec5SDimitry Andric if (byte_size_value > 1) 1790b57cec5SDimitry Andric error.SetErrorStringWithFormat( 1800b57cec5SDimitry Andric "display format (bytes/bytes with ASCII) conflicts with the " 1810b57cec5SDimitry Andric "specified byte size %" PRIu64 "\n" 1820b57cec5SDimitry Andric "\tconsider using a different display format or don't specify " 1830b57cec5SDimitry Andric "the byte size.", 1840b57cec5SDimitry Andric byte_size_value.GetCurrentValue()); 1850b57cec5SDimitry Andric } else 1860b57cec5SDimitry Andric byte_size_value = 1; 1870b57cec5SDimitry Andric if (!num_per_line_option_set) 1880b57cec5SDimitry Andric m_num_per_line = 16; 1890b57cec5SDimitry Andric if (!count_option_set) 1900b57cec5SDimitry Andric format_options.GetCountValue() = 32; 1910b57cec5SDimitry Andric break; 1920b57cec5SDimitry Andric 1930b57cec5SDimitry Andric case eFormatCharArray: 1940b57cec5SDimitry Andric case eFormatChar: 1950b57cec5SDimitry Andric case eFormatCharPrintable: 1960b57cec5SDimitry Andric if (!byte_size_option_set) 1970b57cec5SDimitry Andric byte_size_value = 1; 1980b57cec5SDimitry Andric if (!num_per_line_option_set) 1990b57cec5SDimitry Andric m_num_per_line = 32; 2000b57cec5SDimitry Andric if (!count_option_set) 2010b57cec5SDimitry Andric format_options.GetCountValue() = 64; 2020b57cec5SDimitry Andric break; 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric case eFormatComplex: 2050b57cec5SDimitry Andric if (!byte_size_option_set) 2060b57cec5SDimitry Andric byte_size_value = 8; 2070b57cec5SDimitry Andric if (!num_per_line_option_set) 2080b57cec5SDimitry Andric m_num_per_line = 1; 2090b57cec5SDimitry Andric if (!count_option_set) 2100b57cec5SDimitry Andric format_options.GetCountValue() = 8; 2110b57cec5SDimitry Andric break; 2120b57cec5SDimitry Andric 2130b57cec5SDimitry Andric case eFormatComplexInteger: 2140b57cec5SDimitry Andric if (!byte_size_option_set) 2150b57cec5SDimitry Andric byte_size_value = 8; 2160b57cec5SDimitry Andric if (!num_per_line_option_set) 2170b57cec5SDimitry Andric m_num_per_line = 1; 2180b57cec5SDimitry Andric if (!count_option_set) 2190b57cec5SDimitry Andric format_options.GetCountValue() = 8; 2200b57cec5SDimitry Andric break; 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric case eFormatHex: 2230b57cec5SDimitry Andric if (!byte_size_option_set) 2240b57cec5SDimitry Andric byte_size_value = 4; 2250b57cec5SDimitry Andric if (!num_per_line_option_set) { 2260b57cec5SDimitry Andric switch (byte_size_value) { 2270b57cec5SDimitry Andric case 1: 2280b57cec5SDimitry Andric case 2: 2290b57cec5SDimitry Andric m_num_per_line = 8; 2300b57cec5SDimitry Andric break; 2310b57cec5SDimitry Andric case 4: 2320b57cec5SDimitry Andric m_num_per_line = 4; 2330b57cec5SDimitry Andric break; 2340b57cec5SDimitry Andric case 8: 2350b57cec5SDimitry Andric m_num_per_line = 2; 2360b57cec5SDimitry Andric break; 2370b57cec5SDimitry Andric default: 2380b57cec5SDimitry Andric m_num_per_line = 1; 2390b57cec5SDimitry Andric break; 2400b57cec5SDimitry Andric } 2410b57cec5SDimitry Andric } 2420b57cec5SDimitry Andric if (!count_option_set) 2430b57cec5SDimitry Andric count_value = 8; 2440b57cec5SDimitry Andric break; 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric case eFormatVectorOfChar: 2470b57cec5SDimitry Andric case eFormatVectorOfSInt8: 2480b57cec5SDimitry Andric case eFormatVectorOfUInt8: 2490b57cec5SDimitry Andric case eFormatVectorOfSInt16: 2500b57cec5SDimitry Andric case eFormatVectorOfUInt16: 2510b57cec5SDimitry Andric case eFormatVectorOfSInt32: 2520b57cec5SDimitry Andric case eFormatVectorOfUInt32: 2530b57cec5SDimitry Andric case eFormatVectorOfSInt64: 2540b57cec5SDimitry Andric case eFormatVectorOfUInt64: 2550b57cec5SDimitry Andric case eFormatVectorOfFloat16: 2560b57cec5SDimitry Andric case eFormatVectorOfFloat32: 2570b57cec5SDimitry Andric case eFormatVectorOfFloat64: 2580b57cec5SDimitry Andric case eFormatVectorOfUInt128: 2590b57cec5SDimitry Andric if (!byte_size_option_set) 2600b57cec5SDimitry Andric byte_size_value = 128; 2610b57cec5SDimitry Andric if (!num_per_line_option_set) 2620b57cec5SDimitry Andric m_num_per_line = 1; 2630b57cec5SDimitry Andric if (!count_option_set) 2640b57cec5SDimitry Andric count_value = 4; 2650b57cec5SDimitry Andric break; 2660b57cec5SDimitry Andric } 2670b57cec5SDimitry Andric return error; 2680b57cec5SDimitry Andric } 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric bool AnyOptionWasSet() const { 2710b57cec5SDimitry Andric return m_num_per_line.OptionWasSet() || m_output_as_binary || 2720b57cec5SDimitry Andric m_view_as_type.OptionWasSet() || m_offset.OptionWasSet() || 2730b57cec5SDimitry Andric m_language_for_type.OptionWasSet(); 2740b57cec5SDimitry Andric } 2750b57cec5SDimitry Andric 2760b57cec5SDimitry Andric OptionValueUInt64 m_num_per_line; 277fe6060f1SDimitry Andric bool m_output_as_binary = false; 2780b57cec5SDimitry Andric OptionValueString m_view_as_type; 279fcaf7f86SDimitry Andric bool m_force = false; 2800b57cec5SDimitry Andric OptionValueUInt64 m_offset; 2810b57cec5SDimitry Andric OptionValueLanguage m_language_for_type; 2820b57cec5SDimitry Andric }; 2830b57cec5SDimitry Andric 2840b57cec5SDimitry Andric // Read memory from the inferior process 2850b57cec5SDimitry Andric class CommandObjectMemoryRead : public CommandObjectParsed { 2860b57cec5SDimitry Andric public: 2870b57cec5SDimitry Andric CommandObjectMemoryRead(CommandInterpreter &interpreter) 2880b57cec5SDimitry Andric : CommandObjectParsed( 2890b57cec5SDimitry Andric interpreter, "memory read", 2900b57cec5SDimitry Andric "Read from the memory of the current target process.", nullptr, 2910b57cec5SDimitry Andric eCommandRequiresTarget | eCommandProcessMustBePaused), 29204eeddc0SDimitry Andric m_format_options(eFormatBytesWithASCII, 1, 8), 29381ad6265SDimitry Andric m_memory_tag_options(/*note_binary=*/true), 29404eeddc0SDimitry Andric m_prev_format_options(eFormatBytesWithASCII, 1, 8) { 2950b57cec5SDimitry Andric CommandArgumentEntry arg1; 2960b57cec5SDimitry Andric CommandArgumentEntry arg2; 2970b57cec5SDimitry Andric CommandArgumentData start_addr_arg; 2980b57cec5SDimitry Andric CommandArgumentData end_addr_arg; 2990b57cec5SDimitry Andric 3000b57cec5SDimitry Andric // Define the first (and only) variant of this arg. 3010b57cec5SDimitry Andric start_addr_arg.arg_type = eArgTypeAddressOrExpression; 3020b57cec5SDimitry Andric start_addr_arg.arg_repetition = eArgRepeatPlain; 3030b57cec5SDimitry Andric 3040b57cec5SDimitry Andric // There is only one variant this argument could be; put it into the 3050b57cec5SDimitry Andric // argument entry. 3060b57cec5SDimitry Andric arg1.push_back(start_addr_arg); 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric // Define the first (and only) variant of this arg. 3090b57cec5SDimitry Andric end_addr_arg.arg_type = eArgTypeAddressOrExpression; 3100b57cec5SDimitry Andric end_addr_arg.arg_repetition = eArgRepeatOptional; 3110b57cec5SDimitry Andric 3120b57cec5SDimitry Andric // There is only one variant this argument could be; put it into the 3130b57cec5SDimitry Andric // argument entry. 3140b57cec5SDimitry Andric arg2.push_back(end_addr_arg); 3150b57cec5SDimitry Andric 3160b57cec5SDimitry Andric // Push the data for the first argument into the m_arguments vector. 3170b57cec5SDimitry Andric m_arguments.push_back(arg1); 3180b57cec5SDimitry Andric m_arguments.push_back(arg2); 3190b57cec5SDimitry Andric 3200b57cec5SDimitry Andric // Add the "--format" and "--count" options to group 1 and 3 3210b57cec5SDimitry Andric m_option_group.Append(&m_format_options, 3220b57cec5SDimitry Andric OptionGroupFormat::OPTION_GROUP_FORMAT | 3230b57cec5SDimitry Andric OptionGroupFormat::OPTION_GROUP_COUNT, 3240b57cec5SDimitry Andric LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); 3250b57cec5SDimitry Andric m_option_group.Append(&m_format_options, 3260b57cec5SDimitry Andric OptionGroupFormat::OPTION_GROUP_GDB_FMT, 3270b57cec5SDimitry Andric LLDB_OPT_SET_1 | LLDB_OPT_SET_3); 3280b57cec5SDimitry Andric // Add the "--size" option to group 1 and 2 3290b57cec5SDimitry Andric m_option_group.Append(&m_format_options, 3300b57cec5SDimitry Andric OptionGroupFormat::OPTION_GROUP_SIZE, 3310b57cec5SDimitry Andric LLDB_OPT_SET_1 | LLDB_OPT_SET_2); 3320b57cec5SDimitry Andric m_option_group.Append(&m_memory_options); 3330b57cec5SDimitry Andric m_option_group.Append(&m_outfile_options, LLDB_OPT_SET_ALL, 3340b57cec5SDimitry Andric LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3); 3350b57cec5SDimitry Andric m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3); 33681ad6265SDimitry Andric m_option_group.Append(&m_memory_tag_options, LLDB_OPT_SET_ALL, 33781ad6265SDimitry Andric LLDB_OPT_SET_ALL); 3380b57cec5SDimitry Andric m_option_group.Finalize(); 3390b57cec5SDimitry Andric } 3400b57cec5SDimitry Andric 3410b57cec5SDimitry Andric ~CommandObjectMemoryRead() override = default; 3420b57cec5SDimitry Andric 3430b57cec5SDimitry Andric Options *GetOptions() override { return &m_option_group; } 3440b57cec5SDimitry Andric 345bdd1243dSDimitry Andric std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, 3460b57cec5SDimitry Andric uint32_t index) override { 34781ad6265SDimitry Andric return m_cmd_name; 3480b57cec5SDimitry Andric } 3490b57cec5SDimitry Andric 3500b57cec5SDimitry Andric protected: 3515f757f3fSDimitry Andric void DoExecute(Args &command, CommandReturnObject &result) override { 3520b57cec5SDimitry Andric // No need to check "target" for validity as eCommandRequiresTarget ensures 3530b57cec5SDimitry Andric // it is valid 3540b57cec5SDimitry Andric Target *target = m_exe_ctx.GetTargetPtr(); 3550b57cec5SDimitry Andric 3560b57cec5SDimitry Andric const size_t argc = command.GetArgumentCount(); 3570b57cec5SDimitry Andric 3580b57cec5SDimitry Andric if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2) { 3590b57cec5SDimitry Andric result.AppendErrorWithFormat("%s takes a start address expression with " 3600b57cec5SDimitry Andric "an optional end address expression.\n", 3610b57cec5SDimitry Andric m_cmd_name.c_str()); 362fe6060f1SDimitry Andric result.AppendWarning("Expressions should be quoted if they contain " 363fe6060f1SDimitry Andric "spaces or other special characters."); 3645f757f3fSDimitry Andric return; 3650b57cec5SDimitry Andric } 3660b57cec5SDimitry Andric 3670b57cec5SDimitry Andric CompilerType compiler_type; 3680b57cec5SDimitry Andric Status error; 3690b57cec5SDimitry Andric 3700b57cec5SDimitry Andric const char *view_as_type_cstr = 3710b57cec5SDimitry Andric m_memory_options.m_view_as_type.GetCurrentValue(); 3720b57cec5SDimitry Andric if (view_as_type_cstr && view_as_type_cstr[0]) { 3730b57cec5SDimitry Andric // We are viewing memory as a type 3740b57cec5SDimitry Andric 3750b57cec5SDimitry Andric uint32_t reference_count = 0; 3760b57cec5SDimitry Andric uint32_t pointer_count = 0; 3770b57cec5SDimitry Andric size_t idx; 3780b57cec5SDimitry Andric 3790b57cec5SDimitry Andric #define ALL_KEYWORDS \ 3800b57cec5SDimitry Andric KEYWORD("const") \ 3810b57cec5SDimitry Andric KEYWORD("volatile") \ 3820b57cec5SDimitry Andric KEYWORD("restrict") \ 3830b57cec5SDimitry Andric KEYWORD("struct") \ 3840b57cec5SDimitry Andric KEYWORD("class") \ 3850b57cec5SDimitry Andric KEYWORD("union") 3860b57cec5SDimitry Andric 3870b57cec5SDimitry Andric #define KEYWORD(s) s, 3880b57cec5SDimitry Andric static const char *g_keywords[] = {ALL_KEYWORDS}; 3890b57cec5SDimitry Andric #undef KEYWORD 3900b57cec5SDimitry Andric 3910b57cec5SDimitry Andric #define KEYWORD(s) (sizeof(s) - 1), 3920b57cec5SDimitry Andric static const int g_keyword_lengths[] = {ALL_KEYWORDS}; 3930b57cec5SDimitry Andric #undef KEYWORD 3940b57cec5SDimitry Andric 3950b57cec5SDimitry Andric #undef ALL_KEYWORDS 3960b57cec5SDimitry Andric 3970b57cec5SDimitry Andric static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *); 3980b57cec5SDimitry Andric std::string type_str(view_as_type_cstr); 3990b57cec5SDimitry Andric 4000b57cec5SDimitry Andric // Remove all instances of g_keywords that are followed by spaces 4010b57cec5SDimitry Andric for (size_t i = 0; i < g_num_keywords; ++i) { 4020b57cec5SDimitry Andric const char *keyword = g_keywords[i]; 4030b57cec5SDimitry Andric int keyword_len = g_keyword_lengths[i]; 4040b57cec5SDimitry Andric 4050b57cec5SDimitry Andric idx = 0; 4060b57cec5SDimitry Andric while ((idx = type_str.find(keyword, idx)) != std::string::npos) { 4070b57cec5SDimitry Andric if (type_str[idx + keyword_len] == ' ' || 4080b57cec5SDimitry Andric type_str[idx + keyword_len] == '\t') { 4090b57cec5SDimitry Andric type_str.erase(idx, keyword_len + 1); 4100b57cec5SDimitry Andric idx = 0; 4110b57cec5SDimitry Andric } else { 4120b57cec5SDimitry Andric idx += keyword_len; 4130b57cec5SDimitry Andric } 4140b57cec5SDimitry Andric } 4150b57cec5SDimitry Andric } 4160b57cec5SDimitry Andric bool done = type_str.empty(); 4170b57cec5SDimitry Andric // 4180b57cec5SDimitry Andric idx = type_str.find_first_not_of(" \t"); 4190b57cec5SDimitry Andric if (idx > 0 && idx != std::string::npos) 4200b57cec5SDimitry Andric type_str.erase(0, idx); 4210b57cec5SDimitry Andric while (!done) { 4220b57cec5SDimitry Andric // Strip trailing spaces 4230b57cec5SDimitry Andric if (type_str.empty()) 4240b57cec5SDimitry Andric done = true; 4250b57cec5SDimitry Andric else { 4260b57cec5SDimitry Andric switch (type_str[type_str.size() - 1]) { 4270b57cec5SDimitry Andric case '*': 4280b57cec5SDimitry Andric ++pointer_count; 429bdd1243dSDimitry Andric [[fallthrough]]; 4300b57cec5SDimitry Andric case ' ': 4310b57cec5SDimitry Andric case '\t': 4320b57cec5SDimitry Andric type_str.erase(type_str.size() - 1); 4330b57cec5SDimitry Andric break; 4340b57cec5SDimitry Andric 4350b57cec5SDimitry Andric case '&': 4360b57cec5SDimitry Andric if (reference_count == 0) { 4370b57cec5SDimitry Andric reference_count = 1; 4380b57cec5SDimitry Andric type_str.erase(type_str.size() - 1); 4390b57cec5SDimitry Andric } else { 4400b57cec5SDimitry Andric result.AppendErrorWithFormat("invalid type string: '%s'\n", 4410b57cec5SDimitry Andric view_as_type_cstr); 4425f757f3fSDimitry Andric return; 4430b57cec5SDimitry Andric } 4440b57cec5SDimitry Andric break; 4450b57cec5SDimitry Andric 4460b57cec5SDimitry Andric default: 4470b57cec5SDimitry Andric done = true; 4480b57cec5SDimitry Andric break; 4490b57cec5SDimitry Andric } 4500b57cec5SDimitry Andric } 4510b57cec5SDimitry Andric } 4520b57cec5SDimitry Andric 4530b57cec5SDimitry Andric ConstString lookup_type_name(type_str.c_str()); 4540b57cec5SDimitry Andric StackFrame *frame = m_exe_ctx.GetFramePtr(); 4550b57cec5SDimitry Andric ModuleSP search_first; 4565f757f3fSDimitry Andric if (frame) 4570b57cec5SDimitry Andric search_first = frame->GetSymbolContext(eSymbolContextModule).module_sp; 4585f757f3fSDimitry Andric TypeQuery query(lookup_type_name.GetStringRef(), 4595f757f3fSDimitry Andric TypeQueryOptions::e_find_one); 4605f757f3fSDimitry Andric TypeResults results; 4615f757f3fSDimitry Andric target->GetImages().FindTypes(search_first.get(), query, results); 4625f757f3fSDimitry Andric TypeSP type_sp = results.GetFirstType(); 4630b57cec5SDimitry Andric 4645f757f3fSDimitry Andric if (!type_sp && lookup_type_name.GetCString()) { 4650b57cec5SDimitry Andric LanguageType language_for_type = 4660b57cec5SDimitry Andric m_memory_options.m_language_for_type.GetCurrentValue(); 4670b57cec5SDimitry Andric std::set<LanguageType> languages_to_check; 4680b57cec5SDimitry Andric if (language_for_type != eLanguageTypeUnknown) { 4690b57cec5SDimitry Andric languages_to_check.insert(language_for_type); 4700b57cec5SDimitry Andric } else { 4710b57cec5SDimitry Andric languages_to_check = Language::GetSupportedLanguages(); 4720b57cec5SDimitry Andric } 4730b57cec5SDimitry Andric 4740b57cec5SDimitry Andric std::set<CompilerType> user_defined_types; 4750b57cec5SDimitry Andric for (auto lang : languages_to_check) { 4760b57cec5SDimitry Andric if (auto *persistent_vars = 4770b57cec5SDimitry Andric target->GetPersistentExpressionStateForLanguage(lang)) { 478bdd1243dSDimitry Andric if (std::optional<CompilerType> type = 4790b57cec5SDimitry Andric persistent_vars->GetCompilerTypeFromPersistentDecl( 4800b57cec5SDimitry Andric lookup_type_name)) { 4810b57cec5SDimitry Andric user_defined_types.emplace(*type); 4820b57cec5SDimitry Andric } 4830b57cec5SDimitry Andric } 4840b57cec5SDimitry Andric } 4850b57cec5SDimitry Andric 4860b57cec5SDimitry Andric if (user_defined_types.size() > 1) { 4870b57cec5SDimitry Andric result.AppendErrorWithFormat( 4880b57cec5SDimitry Andric "Mutiple types found matching raw type '%s', please disambiguate " 4890b57cec5SDimitry Andric "by specifying the language with -x", 4900b57cec5SDimitry Andric lookup_type_name.GetCString()); 4915f757f3fSDimitry Andric return; 4920b57cec5SDimitry Andric } 4930b57cec5SDimitry Andric 4940b57cec5SDimitry Andric if (user_defined_types.size() == 1) { 4950b57cec5SDimitry Andric compiler_type = *user_defined_types.begin(); 4960b57cec5SDimitry Andric } 4970b57cec5SDimitry Andric } 4980b57cec5SDimitry Andric 4990b57cec5SDimitry Andric if (!compiler_type.IsValid()) { 5005f757f3fSDimitry Andric if (type_sp) { 5015f757f3fSDimitry Andric compiler_type = type_sp->GetFullCompilerType(); 5025f757f3fSDimitry Andric } else { 5030b57cec5SDimitry Andric result.AppendErrorWithFormat("unable to find any types that match " 5040b57cec5SDimitry Andric "the raw type '%s' for full type '%s'\n", 5050b57cec5SDimitry Andric lookup_type_name.GetCString(), 5060b57cec5SDimitry Andric view_as_type_cstr); 5075f757f3fSDimitry Andric return; 5080b57cec5SDimitry Andric } 5090b57cec5SDimitry Andric } 5100b57cec5SDimitry Andric 5110b57cec5SDimitry Andric while (pointer_count > 0) { 5120b57cec5SDimitry Andric CompilerType pointer_type = compiler_type.GetPointerType(); 5130b57cec5SDimitry Andric if (pointer_type.IsValid()) 5140b57cec5SDimitry Andric compiler_type = pointer_type; 5150b57cec5SDimitry Andric else { 5160b57cec5SDimitry Andric result.AppendError("unable make a pointer type\n"); 5175f757f3fSDimitry Andric return; 5180b57cec5SDimitry Andric } 5190b57cec5SDimitry Andric --pointer_count; 5200b57cec5SDimitry Andric } 5210b57cec5SDimitry Andric 522bdd1243dSDimitry Andric std::optional<uint64_t> size = compiler_type.GetByteSize(nullptr); 5230b57cec5SDimitry Andric if (!size) { 5240b57cec5SDimitry Andric result.AppendErrorWithFormat( 5250b57cec5SDimitry Andric "unable to get the byte size of the type '%s'\n", 5260b57cec5SDimitry Andric view_as_type_cstr); 5275f757f3fSDimitry Andric return; 5280b57cec5SDimitry Andric } 5290b57cec5SDimitry Andric m_format_options.GetByteSizeValue() = *size; 5300b57cec5SDimitry Andric 5310b57cec5SDimitry Andric if (!m_format_options.GetCountValue().OptionWasSet()) 5320b57cec5SDimitry Andric m_format_options.GetCountValue() = 1; 5330b57cec5SDimitry Andric } else { 5340b57cec5SDimitry Andric error = m_memory_options.FinalizeSettings(target, m_format_options); 5350b57cec5SDimitry Andric } 5360b57cec5SDimitry Andric 5370b57cec5SDimitry Andric // Look for invalid combinations of settings 5380b57cec5SDimitry Andric if (error.Fail()) { 5390b57cec5SDimitry Andric result.AppendError(error.AsCString()); 5405f757f3fSDimitry Andric return; 5410b57cec5SDimitry Andric } 5420b57cec5SDimitry Andric 5430b57cec5SDimitry Andric lldb::addr_t addr; 5440b57cec5SDimitry Andric size_t total_byte_size = 0; 5450b57cec5SDimitry Andric if (argc == 0) { 5460b57cec5SDimitry Andric // Use the last address and byte size and all options as they were if no 5470b57cec5SDimitry Andric // options have been set 5480b57cec5SDimitry Andric addr = m_next_addr; 5490b57cec5SDimitry Andric total_byte_size = m_prev_byte_size; 5500b57cec5SDimitry Andric compiler_type = m_prev_compiler_type; 5510b57cec5SDimitry Andric if (!m_format_options.AnyOptionWasSet() && 5520b57cec5SDimitry Andric !m_memory_options.AnyOptionWasSet() && 5530b57cec5SDimitry Andric !m_outfile_options.AnyOptionWasSet() && 55481ad6265SDimitry Andric !m_varobj_options.AnyOptionWasSet() && 55581ad6265SDimitry Andric !m_memory_tag_options.AnyOptionWasSet()) { 5560b57cec5SDimitry Andric m_format_options = m_prev_format_options; 5570b57cec5SDimitry Andric m_memory_options = m_prev_memory_options; 5580b57cec5SDimitry Andric m_outfile_options = m_prev_outfile_options; 5590b57cec5SDimitry Andric m_varobj_options = m_prev_varobj_options; 56081ad6265SDimitry Andric m_memory_tag_options = m_prev_memory_tag_options; 5610b57cec5SDimitry Andric } 5620b57cec5SDimitry Andric } 5630b57cec5SDimitry Andric 5640b57cec5SDimitry Andric size_t item_count = m_format_options.GetCountValue().GetCurrentValue(); 5650b57cec5SDimitry Andric 5660b57cec5SDimitry Andric // TODO For non-8-bit byte addressable architectures this needs to be 5670b57cec5SDimitry Andric // revisited to fully support all lldb's range of formatting options. 5680b57cec5SDimitry Andric // Furthermore code memory reads (for those architectures) will not be 5690b57cec5SDimitry Andric // correctly formatted even w/o formatting options. 5700b57cec5SDimitry Andric size_t item_byte_size = 5710b57cec5SDimitry Andric target->GetArchitecture().GetDataByteSize() > 1 5720b57cec5SDimitry Andric ? target->GetArchitecture().GetDataByteSize() 5730b57cec5SDimitry Andric : m_format_options.GetByteSizeValue().GetCurrentValue(); 5740b57cec5SDimitry Andric 5750b57cec5SDimitry Andric const size_t num_per_line = 5760b57cec5SDimitry Andric m_memory_options.m_num_per_line.GetCurrentValue(); 5770b57cec5SDimitry Andric 5780b57cec5SDimitry Andric if (total_byte_size == 0) { 5790b57cec5SDimitry Andric total_byte_size = item_count * item_byte_size; 5800b57cec5SDimitry Andric if (total_byte_size == 0) 5810b57cec5SDimitry Andric total_byte_size = 32; 5820b57cec5SDimitry Andric } 5830b57cec5SDimitry Andric 5840b57cec5SDimitry Andric if (argc > 0) 5859dba64beSDimitry Andric addr = OptionArgParser::ToAddress(&m_exe_ctx, command[0].ref(), 5860b57cec5SDimitry Andric LLDB_INVALID_ADDRESS, &error); 5870b57cec5SDimitry Andric 5880b57cec5SDimitry Andric if (addr == LLDB_INVALID_ADDRESS) { 5890b57cec5SDimitry Andric result.AppendError("invalid start address expression."); 5900b57cec5SDimitry Andric result.AppendError(error.AsCString()); 5915f757f3fSDimitry Andric return; 5920b57cec5SDimitry Andric } 5930b57cec5SDimitry Andric 5940b57cec5SDimitry Andric if (argc == 2) { 5950b57cec5SDimitry Andric lldb::addr_t end_addr = OptionArgParser::ToAddress( 5969dba64beSDimitry Andric &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, nullptr); 59704eeddc0SDimitry Andric 5980b57cec5SDimitry Andric if (end_addr == LLDB_INVALID_ADDRESS) { 5990b57cec5SDimitry Andric result.AppendError("invalid end address expression."); 6000b57cec5SDimitry Andric result.AppendError(error.AsCString()); 6015f757f3fSDimitry Andric return; 6020b57cec5SDimitry Andric } else if (end_addr <= addr) { 6030b57cec5SDimitry Andric result.AppendErrorWithFormat( 6040b57cec5SDimitry Andric "end address (0x%" PRIx64 605fe6060f1SDimitry Andric ") must be greater than the start address (0x%" PRIx64 ").\n", 6060b57cec5SDimitry Andric end_addr, addr); 6075f757f3fSDimitry Andric return; 6080b57cec5SDimitry Andric } else if (m_format_options.GetCountValue().OptionWasSet()) { 6090b57cec5SDimitry Andric result.AppendErrorWithFormat( 6100b57cec5SDimitry Andric "specify either the end address (0x%" PRIx64 6110b57cec5SDimitry Andric ") or the count (--count %" PRIu64 "), not both.\n", 6120b57cec5SDimitry Andric end_addr, (uint64_t)item_count); 6135f757f3fSDimitry Andric return; 6140b57cec5SDimitry Andric } 6150b57cec5SDimitry Andric 6160b57cec5SDimitry Andric total_byte_size = end_addr - addr; 6170b57cec5SDimitry Andric item_count = total_byte_size / item_byte_size; 6180b57cec5SDimitry Andric } 6190b57cec5SDimitry Andric 6200b57cec5SDimitry Andric uint32_t max_unforced_size = target->GetMaximumMemReadSize(); 6210b57cec5SDimitry Andric 6220b57cec5SDimitry Andric if (total_byte_size > max_unforced_size && !m_memory_options.m_force) { 6230b57cec5SDimitry Andric result.AppendErrorWithFormat( 6240b57cec5SDimitry Andric "Normally, \'memory read\' will not read over %" PRIu32 6250b57cec5SDimitry Andric " bytes of data.\n", 6260b57cec5SDimitry Andric max_unforced_size); 6270b57cec5SDimitry Andric result.AppendErrorWithFormat( 6280b57cec5SDimitry Andric "Please use --force to override this restriction just once.\n"); 6290b57cec5SDimitry Andric result.AppendErrorWithFormat("or set target.max-memory-read-size if you " 6300b57cec5SDimitry Andric "will often need a larger limit.\n"); 6315f757f3fSDimitry Andric return; 6320b57cec5SDimitry Andric } 6330b57cec5SDimitry Andric 63481ad6265SDimitry Andric WritableDataBufferSP data_sp; 6350b57cec5SDimitry Andric size_t bytes_read = 0; 6360b57cec5SDimitry Andric if (compiler_type.GetOpaqueQualType()) { 6370b57cec5SDimitry Andric // Make sure we don't display our type as ASCII bytes like the default 6380b57cec5SDimitry Andric // memory read 6390b57cec5SDimitry Andric if (!m_format_options.GetFormatValue().OptionWasSet()) 6400b57cec5SDimitry Andric m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault); 6410b57cec5SDimitry Andric 642bdd1243dSDimitry Andric std::optional<uint64_t> size = compiler_type.GetByteSize(nullptr); 6430b57cec5SDimitry Andric if (!size) { 6440b57cec5SDimitry Andric result.AppendError("can't get size of type"); 6455f757f3fSDimitry Andric return; 6460b57cec5SDimitry Andric } 6470b57cec5SDimitry Andric bytes_read = *size * m_format_options.GetCountValue().GetCurrentValue(); 6480b57cec5SDimitry Andric 6490b57cec5SDimitry Andric if (argc > 0) 6500b57cec5SDimitry Andric addr = addr + (*size * m_memory_options.m_offset.GetCurrentValue()); 6510b57cec5SDimitry Andric } else if (m_format_options.GetFormatValue().GetCurrentValue() != 6520b57cec5SDimitry Andric eFormatCString) { 6530b57cec5SDimitry Andric data_sp = std::make_shared<DataBufferHeap>(total_byte_size, '\0'); 6540b57cec5SDimitry Andric if (data_sp->GetBytes() == nullptr) { 6550b57cec5SDimitry Andric result.AppendErrorWithFormat( 6560b57cec5SDimitry Andric "can't allocate 0x%" PRIx32 6570b57cec5SDimitry Andric " bytes for the memory read buffer, specify a smaller size to read", 6580b57cec5SDimitry Andric (uint32_t)total_byte_size); 6595f757f3fSDimitry Andric return; 6600b57cec5SDimitry Andric } 6610b57cec5SDimitry Andric 6620b57cec5SDimitry Andric Address address(addr, nullptr); 663fe6060f1SDimitry Andric bytes_read = target->ReadMemory(address, data_sp->GetBytes(), 664fe6060f1SDimitry Andric data_sp->GetByteSize(), error, true); 6650b57cec5SDimitry Andric if (bytes_read == 0) { 6660b57cec5SDimitry Andric const char *error_cstr = error.AsCString(); 6670b57cec5SDimitry Andric if (error_cstr && error_cstr[0]) { 6680b57cec5SDimitry Andric result.AppendError(error_cstr); 6690b57cec5SDimitry Andric } else { 6700b57cec5SDimitry Andric result.AppendErrorWithFormat( 6710b57cec5SDimitry Andric "failed to read memory from 0x%" PRIx64 ".\n", addr); 6720b57cec5SDimitry Andric } 6735f757f3fSDimitry Andric return; 6740b57cec5SDimitry Andric } 6750b57cec5SDimitry Andric 6760b57cec5SDimitry Andric if (bytes_read < total_byte_size) 6770b57cec5SDimitry Andric result.AppendWarningWithFormat( 6780b57cec5SDimitry Andric "Not all bytes (%" PRIu64 "/%" PRIu64 6790b57cec5SDimitry Andric ") were able to be read from 0x%" PRIx64 ".\n", 6800b57cec5SDimitry Andric (uint64_t)bytes_read, (uint64_t)total_byte_size, addr); 6810b57cec5SDimitry Andric } else { 6820b57cec5SDimitry Andric // we treat c-strings as a special case because they do not have a fixed 6830b57cec5SDimitry Andric // size 6840b57cec5SDimitry Andric if (m_format_options.GetByteSizeValue().OptionWasSet() && 6850b57cec5SDimitry Andric !m_format_options.HasGDBFormat()) 6860b57cec5SDimitry Andric item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue(); 6870b57cec5SDimitry Andric else 6880b57cec5SDimitry Andric item_byte_size = target->GetMaximumSizeOfStringSummary(); 6890b57cec5SDimitry Andric if (!m_format_options.GetCountValue().OptionWasSet()) 6900b57cec5SDimitry Andric item_count = 1; 6910b57cec5SDimitry Andric data_sp = std::make_shared<DataBufferHeap>( 6920b57cec5SDimitry Andric (item_byte_size + 1) * item_count, 6930b57cec5SDimitry Andric '\0'); // account for NULLs as necessary 6940b57cec5SDimitry Andric if (data_sp->GetBytes() == nullptr) { 6950b57cec5SDimitry Andric result.AppendErrorWithFormat( 6960b57cec5SDimitry Andric "can't allocate 0x%" PRIx64 6970b57cec5SDimitry Andric " bytes for the memory read buffer, specify a smaller size to read", 6980b57cec5SDimitry Andric (uint64_t)((item_byte_size + 1) * item_count)); 6995f757f3fSDimitry Andric return; 7000b57cec5SDimitry Andric } 7010b57cec5SDimitry Andric uint8_t *data_ptr = data_sp->GetBytes(); 7020b57cec5SDimitry Andric auto data_addr = addr; 7030b57cec5SDimitry Andric auto count = item_count; 7040b57cec5SDimitry Andric item_count = 0; 7050b57cec5SDimitry Andric bool break_on_no_NULL = false; 7060b57cec5SDimitry Andric while (item_count < count) { 7070b57cec5SDimitry Andric std::string buffer; 7080b57cec5SDimitry Andric buffer.resize(item_byte_size + 1, 0); 7090b57cec5SDimitry Andric Status error; 7100b57cec5SDimitry Andric size_t read = target->ReadCStringFromMemory(data_addr, &buffer[0], 7110b57cec5SDimitry Andric item_byte_size + 1, error); 7120b57cec5SDimitry Andric if (error.Fail()) { 7130b57cec5SDimitry Andric result.AppendErrorWithFormat( 7140b57cec5SDimitry Andric "failed to read memory from 0x%" PRIx64 ".\n", addr); 7155f757f3fSDimitry Andric return; 7160b57cec5SDimitry Andric } 7170b57cec5SDimitry Andric 7180b57cec5SDimitry Andric if (item_byte_size == read) { 7190b57cec5SDimitry Andric result.AppendWarningWithFormat( 7200b57cec5SDimitry Andric "unable to find a NULL terminated string at 0x%" PRIx64 7210b57cec5SDimitry Andric ". Consider increasing the maximum read length.\n", 7220b57cec5SDimitry Andric data_addr); 7230b57cec5SDimitry Andric --read; 7240b57cec5SDimitry Andric break_on_no_NULL = true; 7250b57cec5SDimitry Andric } else 7260b57cec5SDimitry Andric ++read; // account for final NULL byte 7270b57cec5SDimitry Andric 7280b57cec5SDimitry Andric memcpy(data_ptr, &buffer[0], read); 7290b57cec5SDimitry Andric data_ptr += read; 7300b57cec5SDimitry Andric data_addr += read; 7310b57cec5SDimitry Andric bytes_read += read; 7320b57cec5SDimitry Andric item_count++; // if we break early we know we only read item_count 7330b57cec5SDimitry Andric // strings 7340b57cec5SDimitry Andric 7350b57cec5SDimitry Andric if (break_on_no_NULL) 7360b57cec5SDimitry Andric break; 7370b57cec5SDimitry Andric } 7380b57cec5SDimitry Andric data_sp = 7390b57cec5SDimitry Andric std::make_shared<DataBufferHeap>(data_sp->GetBytes(), bytes_read + 1); 7400b57cec5SDimitry Andric } 7410b57cec5SDimitry Andric 7420b57cec5SDimitry Andric m_next_addr = addr + bytes_read; 7430b57cec5SDimitry Andric m_prev_byte_size = bytes_read; 7440b57cec5SDimitry Andric m_prev_format_options = m_format_options; 7450b57cec5SDimitry Andric m_prev_memory_options = m_memory_options; 7460b57cec5SDimitry Andric m_prev_outfile_options = m_outfile_options; 7470b57cec5SDimitry Andric m_prev_varobj_options = m_varobj_options; 74881ad6265SDimitry Andric m_prev_memory_tag_options = m_memory_tag_options; 7490b57cec5SDimitry Andric m_prev_compiler_type = compiler_type; 7500b57cec5SDimitry Andric 7519dba64beSDimitry Andric std::unique_ptr<Stream> output_stream_storage; 7529dba64beSDimitry Andric Stream *output_stream_p = nullptr; 7530b57cec5SDimitry Andric const FileSpec &outfile_spec = 7540b57cec5SDimitry Andric m_outfile_options.GetFile().GetCurrentValue(); 7550b57cec5SDimitry Andric 7560b57cec5SDimitry Andric std::string path = outfile_spec.GetPath(); 7570b57cec5SDimitry Andric if (outfile_spec) { 7580b57cec5SDimitry Andric 759fe6060f1SDimitry Andric File::OpenOptions open_options = 760349cc55cSDimitry Andric File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate; 7610b57cec5SDimitry Andric const bool append = m_outfile_options.GetAppend().GetCurrentValue(); 762fe6060f1SDimitry Andric open_options |= 763fe6060f1SDimitry Andric append ? File::eOpenOptionAppend : File::eOpenOptionTruncate; 7640b57cec5SDimitry Andric 7659dba64beSDimitry Andric auto outfile = FileSystem::Instance().Open(outfile_spec, open_options); 7669dba64beSDimitry Andric 7679dba64beSDimitry Andric if (outfile) { 7689dba64beSDimitry Andric auto outfile_stream_up = 7699dba64beSDimitry Andric std::make_unique<StreamFile>(std::move(outfile.get())); 7700b57cec5SDimitry Andric if (m_memory_options.m_output_as_binary) { 7710b57cec5SDimitry Andric const size_t bytes_written = 7729dba64beSDimitry Andric outfile_stream_up->Write(data_sp->GetBytes(), bytes_read); 7730b57cec5SDimitry Andric if (bytes_written > 0) { 7740b57cec5SDimitry Andric result.GetOutputStream().Printf( 7750b57cec5SDimitry Andric "%zi bytes %s to '%s'\n", bytes_written, 7760b57cec5SDimitry Andric append ? "appended" : "written", path.c_str()); 7775f757f3fSDimitry Andric return; 7780b57cec5SDimitry Andric } else { 7790b57cec5SDimitry Andric result.AppendErrorWithFormat("Failed to write %" PRIu64 7800b57cec5SDimitry Andric " bytes to '%s'.\n", 7810b57cec5SDimitry Andric (uint64_t)bytes_read, path.c_str()); 7825f757f3fSDimitry Andric return; 7830b57cec5SDimitry Andric } 7840b57cec5SDimitry Andric } else { 7850b57cec5SDimitry Andric // We are going to write ASCII to the file just point the 7860b57cec5SDimitry Andric // output_stream to our outfile_stream... 7879dba64beSDimitry Andric output_stream_storage = std::move(outfile_stream_up); 7889dba64beSDimitry Andric output_stream_p = output_stream_storage.get(); 7890b57cec5SDimitry Andric } 7900b57cec5SDimitry Andric } else { 7919dba64beSDimitry Andric result.AppendErrorWithFormat("Failed to open file '%s' for %s:\n", 7920b57cec5SDimitry Andric path.c_str(), append ? "append" : "write"); 7939dba64beSDimitry Andric 7949dba64beSDimitry Andric result.AppendError(llvm::toString(outfile.takeError())); 7955f757f3fSDimitry Andric return; 7960b57cec5SDimitry Andric } 7970b57cec5SDimitry Andric } else { 7989dba64beSDimitry Andric output_stream_p = &result.GetOutputStream(); 7990b57cec5SDimitry Andric } 8000b57cec5SDimitry Andric 8010b57cec5SDimitry Andric ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); 8020b57cec5SDimitry Andric if (compiler_type.GetOpaqueQualType()) { 8030b57cec5SDimitry Andric for (uint32_t i = 0; i < item_count; ++i) { 8040b57cec5SDimitry Andric addr_t item_addr = addr + (i * item_byte_size); 8050b57cec5SDimitry Andric Address address(item_addr); 8060b57cec5SDimitry Andric StreamString name_strm; 8070b57cec5SDimitry Andric name_strm.Printf("0x%" PRIx64, item_addr); 8080b57cec5SDimitry Andric ValueObjectSP valobj_sp(ValueObjectMemory::Create( 8090b57cec5SDimitry Andric exe_scope, name_strm.GetString(), address, compiler_type)); 8100b57cec5SDimitry Andric if (valobj_sp) { 8110b57cec5SDimitry Andric Format format = m_format_options.GetFormat(); 8120b57cec5SDimitry Andric if (format != eFormatDefault) 8130b57cec5SDimitry Andric valobj_sp->SetFormat(format); 8140b57cec5SDimitry Andric 8150b57cec5SDimitry Andric DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( 8160b57cec5SDimitry Andric eLanguageRuntimeDescriptionDisplayVerbosityFull, format)); 8170b57cec5SDimitry Andric 818*0fca6ea1SDimitry Andric if (llvm::Error error = valobj_sp->Dump(*output_stream_p, options)) { 819*0fca6ea1SDimitry Andric result.AppendError(toString(std::move(error))); 820*0fca6ea1SDimitry Andric return; 821*0fca6ea1SDimitry Andric } 8220b57cec5SDimitry Andric } else { 8230b57cec5SDimitry Andric result.AppendErrorWithFormat( 8240b57cec5SDimitry Andric "failed to create a value object for: (%s) %s\n", 8250b57cec5SDimitry Andric view_as_type_cstr, name_strm.GetData()); 8265f757f3fSDimitry Andric return; 8270b57cec5SDimitry Andric } 8280b57cec5SDimitry Andric } 8295f757f3fSDimitry Andric return; 8300b57cec5SDimitry Andric } 8310b57cec5SDimitry Andric 8320b57cec5SDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 8330b57cec5SDimitry Andric DataExtractor data(data_sp, target->GetArchitecture().GetByteOrder(), 8340b57cec5SDimitry Andric target->GetArchitecture().GetAddressByteSize(), 8350b57cec5SDimitry Andric target->GetArchitecture().GetDataByteSize()); 8360b57cec5SDimitry Andric 8370b57cec5SDimitry Andric Format format = m_format_options.GetFormat(); 8380b57cec5SDimitry Andric if (((format == eFormatChar) || (format == eFormatCharPrintable)) && 8390b57cec5SDimitry Andric (item_byte_size != 1)) { 8400b57cec5SDimitry Andric // if a count was not passed, or it is 1 8410b57cec5SDimitry Andric if (!m_format_options.GetCountValue().OptionWasSet() || item_count == 1) { 8420b57cec5SDimitry Andric // this turns requests such as 8430b57cec5SDimitry Andric // memory read -fc -s10 -c1 *charPtrPtr 8440b57cec5SDimitry Andric // which make no sense (what is a char of size 10?) into a request for 8450b57cec5SDimitry Andric // fetching 10 chars of size 1 from the same memory location 8460b57cec5SDimitry Andric format = eFormatCharArray; 8470b57cec5SDimitry Andric item_count = item_byte_size; 8480b57cec5SDimitry Andric item_byte_size = 1; 8490b57cec5SDimitry Andric } else { 8500b57cec5SDimitry Andric // here we passed a count, and it was not 1 so we have a byte_size and 8510b57cec5SDimitry Andric // a count we could well multiply those, but instead let's just fail 8520b57cec5SDimitry Andric result.AppendErrorWithFormat( 8530b57cec5SDimitry Andric "reading memory as characters of size %" PRIu64 " is not supported", 8540b57cec5SDimitry Andric (uint64_t)item_byte_size); 8555f757f3fSDimitry Andric return; 8560b57cec5SDimitry Andric } 8570b57cec5SDimitry Andric } 8580b57cec5SDimitry Andric 8599dba64beSDimitry Andric assert(output_stream_p); 8600b57cec5SDimitry Andric size_t bytes_dumped = DumpDataExtractor( 8619dba64beSDimitry Andric data, output_stream_p, 0, format, item_byte_size, item_count, 8620b57cec5SDimitry Andric num_per_line / target->GetArchitecture().GetDataByteSize(), addr, 0, 0, 86381ad6265SDimitry Andric exe_scope, m_memory_tag_options.GetShowTags().GetCurrentValue()); 8640b57cec5SDimitry Andric m_next_addr = addr + bytes_dumped; 8659dba64beSDimitry Andric output_stream_p->EOL(); 8660b57cec5SDimitry Andric } 8670b57cec5SDimitry Andric 8680b57cec5SDimitry Andric OptionGroupOptions m_option_group; 8690b57cec5SDimitry Andric OptionGroupFormat m_format_options; 8700b57cec5SDimitry Andric OptionGroupReadMemory m_memory_options; 8710b57cec5SDimitry Andric OptionGroupOutputFile m_outfile_options; 8720b57cec5SDimitry Andric OptionGroupValueObjectDisplay m_varobj_options; 87381ad6265SDimitry Andric OptionGroupMemoryTag m_memory_tag_options; 87481ad6265SDimitry Andric lldb::addr_t m_next_addr = LLDB_INVALID_ADDRESS; 87581ad6265SDimitry Andric lldb::addr_t m_prev_byte_size = 0; 8760b57cec5SDimitry Andric OptionGroupFormat m_prev_format_options; 8770b57cec5SDimitry Andric OptionGroupReadMemory m_prev_memory_options; 8780b57cec5SDimitry Andric OptionGroupOutputFile m_prev_outfile_options; 8790b57cec5SDimitry Andric OptionGroupValueObjectDisplay m_prev_varobj_options; 88081ad6265SDimitry Andric OptionGroupMemoryTag m_prev_memory_tag_options; 8810b57cec5SDimitry Andric CompilerType m_prev_compiler_type; 8820b57cec5SDimitry Andric }; 8830b57cec5SDimitry Andric 8849dba64beSDimitry Andric #define LLDB_OPTIONS_memory_find 8859dba64beSDimitry Andric #include "CommandOptions.inc" 8860b57cec5SDimitry Andric 8870b57cec5SDimitry Andric // Find the specified data in memory 8880b57cec5SDimitry Andric class CommandObjectMemoryFind : public CommandObjectParsed { 8890b57cec5SDimitry Andric public: 8900b57cec5SDimitry Andric class OptionGroupFindMemory : public OptionGroup { 8910b57cec5SDimitry Andric public: 89204eeddc0SDimitry Andric OptionGroupFindMemory() : m_count(1), m_offset(0) {} 8930b57cec5SDimitry Andric 8940b57cec5SDimitry Andric ~OptionGroupFindMemory() override = default; 8950b57cec5SDimitry Andric 8960b57cec5SDimitry Andric llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 897bdd1243dSDimitry Andric return llvm::ArrayRef(g_memory_find_options); 8980b57cec5SDimitry Andric } 8990b57cec5SDimitry Andric 9000b57cec5SDimitry Andric Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, 9010b57cec5SDimitry Andric ExecutionContext *execution_context) override { 9020b57cec5SDimitry Andric Status error; 9039dba64beSDimitry Andric const int short_option = g_memory_find_options[option_idx].short_option; 9040b57cec5SDimitry Andric 9050b57cec5SDimitry Andric switch (short_option) { 9060b57cec5SDimitry Andric case 'e': 9070b57cec5SDimitry Andric m_expr.SetValueFromString(option_value); 9080b57cec5SDimitry Andric break; 9090b57cec5SDimitry Andric 9100b57cec5SDimitry Andric case 's': 9110b57cec5SDimitry Andric m_string.SetValueFromString(option_value); 9120b57cec5SDimitry Andric break; 9130b57cec5SDimitry Andric 9140b57cec5SDimitry Andric case 'c': 9150b57cec5SDimitry Andric if (m_count.SetValueFromString(option_value).Fail()) 9160b57cec5SDimitry Andric error.SetErrorString("unrecognized value for count"); 9170b57cec5SDimitry Andric break; 9180b57cec5SDimitry Andric 9190b57cec5SDimitry Andric case 'o': 9200b57cec5SDimitry Andric if (m_offset.SetValueFromString(option_value).Fail()) 9210b57cec5SDimitry Andric error.SetErrorString("unrecognized value for dump-offset"); 9220b57cec5SDimitry Andric break; 9230b57cec5SDimitry Andric 9240b57cec5SDimitry Andric default: 9259dba64beSDimitry Andric llvm_unreachable("Unimplemented option"); 9260b57cec5SDimitry Andric } 9270b57cec5SDimitry Andric return error; 9280b57cec5SDimitry Andric } 9290b57cec5SDimitry Andric 9300b57cec5SDimitry Andric void OptionParsingStarting(ExecutionContext *execution_context) override { 9310b57cec5SDimitry Andric m_expr.Clear(); 9320b57cec5SDimitry Andric m_string.Clear(); 9330b57cec5SDimitry Andric m_count.Clear(); 9340b57cec5SDimitry Andric } 9350b57cec5SDimitry Andric 9360b57cec5SDimitry Andric OptionValueString m_expr; 9370b57cec5SDimitry Andric OptionValueString m_string; 9380b57cec5SDimitry Andric OptionValueUInt64 m_count; 9390b57cec5SDimitry Andric OptionValueUInt64 m_offset; 9400b57cec5SDimitry Andric }; 9410b57cec5SDimitry Andric 9420b57cec5SDimitry Andric CommandObjectMemoryFind(CommandInterpreter &interpreter) 9430b57cec5SDimitry Andric : CommandObjectParsed( 9440b57cec5SDimitry Andric interpreter, "memory find", 9450b57cec5SDimitry Andric "Find a value in the memory of the current target process.", 94604eeddc0SDimitry Andric nullptr, eCommandRequiresProcess | eCommandProcessMustBeLaunched) { 9470b57cec5SDimitry Andric CommandArgumentEntry arg1; 9480b57cec5SDimitry Andric CommandArgumentEntry arg2; 9490b57cec5SDimitry Andric CommandArgumentData addr_arg; 9500b57cec5SDimitry Andric CommandArgumentData value_arg; 9510b57cec5SDimitry Andric 9520b57cec5SDimitry Andric // Define the first (and only) variant of this arg. 9530b57cec5SDimitry Andric addr_arg.arg_type = eArgTypeAddressOrExpression; 9540b57cec5SDimitry Andric addr_arg.arg_repetition = eArgRepeatPlain; 9550b57cec5SDimitry Andric 9560b57cec5SDimitry Andric // There is only one variant this argument could be; put it into the 9570b57cec5SDimitry Andric // argument entry. 9580b57cec5SDimitry Andric arg1.push_back(addr_arg); 9590b57cec5SDimitry Andric 9600b57cec5SDimitry Andric // Define the first (and only) variant of this arg. 9610b57cec5SDimitry Andric value_arg.arg_type = eArgTypeAddressOrExpression; 9620b57cec5SDimitry Andric value_arg.arg_repetition = eArgRepeatPlain; 9630b57cec5SDimitry Andric 9640b57cec5SDimitry Andric // There is only one variant this argument could be; put it into the 9650b57cec5SDimitry Andric // argument entry. 9660b57cec5SDimitry Andric arg2.push_back(value_arg); 9670b57cec5SDimitry Andric 9680b57cec5SDimitry Andric // Push the data for the first argument into the m_arguments vector. 9690b57cec5SDimitry Andric m_arguments.push_back(arg1); 9700b57cec5SDimitry Andric m_arguments.push_back(arg2); 9710b57cec5SDimitry Andric 9720b57cec5SDimitry Andric m_option_group.Append(&m_memory_options); 97381ad6265SDimitry Andric m_option_group.Append(&m_memory_tag_options, LLDB_OPT_SET_ALL, 97481ad6265SDimitry Andric LLDB_OPT_SET_ALL); 9750b57cec5SDimitry Andric m_option_group.Finalize(); 9760b57cec5SDimitry Andric } 9770b57cec5SDimitry Andric 9780b57cec5SDimitry Andric ~CommandObjectMemoryFind() override = default; 9790b57cec5SDimitry Andric 9800b57cec5SDimitry Andric Options *GetOptions() override { return &m_option_group; } 9810b57cec5SDimitry Andric 9820b57cec5SDimitry Andric protected: 9835f757f3fSDimitry Andric void DoExecute(Args &command, CommandReturnObject &result) override { 9840b57cec5SDimitry Andric // No need to check "process" for validity as eCommandRequiresProcess 9850b57cec5SDimitry Andric // ensures it is valid 9860b57cec5SDimitry Andric Process *process = m_exe_ctx.GetProcessPtr(); 9870b57cec5SDimitry Andric 9880b57cec5SDimitry Andric const size_t argc = command.GetArgumentCount(); 9890b57cec5SDimitry Andric 9900b57cec5SDimitry Andric if (argc != 2) { 9910b57cec5SDimitry Andric result.AppendError("two addresses needed for memory find"); 9925f757f3fSDimitry Andric return; 9930b57cec5SDimitry Andric } 9940b57cec5SDimitry Andric 9950b57cec5SDimitry Andric Status error; 9960b57cec5SDimitry Andric lldb::addr_t low_addr = OptionArgParser::ToAddress( 9979dba64beSDimitry Andric &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); 9980b57cec5SDimitry Andric if (low_addr == LLDB_INVALID_ADDRESS || error.Fail()) { 9990b57cec5SDimitry Andric result.AppendError("invalid low address"); 10005f757f3fSDimitry Andric return; 10010b57cec5SDimitry Andric } 10020b57cec5SDimitry Andric lldb::addr_t high_addr = OptionArgParser::ToAddress( 10039dba64beSDimitry Andric &m_exe_ctx, command[1].ref(), LLDB_INVALID_ADDRESS, &error); 10040b57cec5SDimitry Andric if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) { 10050b57cec5SDimitry Andric result.AppendError("invalid high address"); 10065f757f3fSDimitry Andric return; 10070b57cec5SDimitry Andric } 10080b57cec5SDimitry Andric 10090b57cec5SDimitry Andric if (high_addr <= low_addr) { 10100b57cec5SDimitry Andric result.AppendError( 10110b57cec5SDimitry Andric "starting address must be smaller than ending address"); 10125f757f3fSDimitry Andric return; 10130b57cec5SDimitry Andric } 10140b57cec5SDimitry Andric 10150b57cec5SDimitry Andric lldb::addr_t found_location = LLDB_INVALID_ADDRESS; 10160b57cec5SDimitry Andric 10170b57cec5SDimitry Andric DataBufferHeap buffer; 10180b57cec5SDimitry Andric 101981ad6265SDimitry Andric if (m_memory_options.m_string.OptionWasSet()) { 102006c3fb27SDimitry Andric llvm::StringRef str = 102106c3fb27SDimitry Andric m_memory_options.m_string.GetValueAs<llvm::StringRef>().value_or(""); 102281ad6265SDimitry Andric if (str.empty()) { 102381ad6265SDimitry Andric result.AppendError("search string must have non-zero length."); 10245f757f3fSDimitry Andric return; 102581ad6265SDimitry Andric } 102681ad6265SDimitry Andric buffer.CopyData(str); 102781ad6265SDimitry Andric } else if (m_memory_options.m_expr.OptionWasSet()) { 10280b57cec5SDimitry Andric StackFrame *frame = m_exe_ctx.GetFramePtr(); 10290b57cec5SDimitry Andric ValueObjectSP result_sp; 10300b57cec5SDimitry Andric if ((eExpressionCompleted == 10310b57cec5SDimitry Andric process->GetTarget().EvaluateExpression( 103206c3fb27SDimitry Andric m_memory_options.m_expr.GetValueAs<llvm::StringRef>().value_or( 103306c3fb27SDimitry Andric ""), 103406c3fb27SDimitry Andric frame, result_sp)) && 10350b57cec5SDimitry Andric result_sp) { 10360b57cec5SDimitry Andric uint64_t value = result_sp->GetValueAsUnsigned(0); 1037bdd1243dSDimitry Andric std::optional<uint64_t> size = 10380b57cec5SDimitry Andric result_sp->GetCompilerType().GetByteSize(nullptr); 10390b57cec5SDimitry Andric if (!size) 10405f757f3fSDimitry Andric return; 10410b57cec5SDimitry Andric switch (*size) { 10420b57cec5SDimitry Andric case 1: { 10430b57cec5SDimitry Andric uint8_t byte = (uint8_t)value; 10440b57cec5SDimitry Andric buffer.CopyData(&byte, 1); 10450b57cec5SDimitry Andric } break; 10460b57cec5SDimitry Andric case 2: { 10470b57cec5SDimitry Andric uint16_t word = (uint16_t)value; 10480b57cec5SDimitry Andric buffer.CopyData(&word, 2); 10490b57cec5SDimitry Andric } break; 10500b57cec5SDimitry Andric case 4: { 10510b57cec5SDimitry Andric uint32_t lword = (uint32_t)value; 10520b57cec5SDimitry Andric buffer.CopyData(&lword, 4); 10530b57cec5SDimitry Andric } break; 10540b57cec5SDimitry Andric case 8: { 10550b57cec5SDimitry Andric buffer.CopyData(&value, 8); 10560b57cec5SDimitry Andric } break; 10570b57cec5SDimitry Andric case 3: 10580b57cec5SDimitry Andric case 5: 10590b57cec5SDimitry Andric case 6: 10600b57cec5SDimitry Andric case 7: 10610b57cec5SDimitry Andric result.AppendError("unknown type. pass a string instead"); 10625f757f3fSDimitry Andric return; 10630b57cec5SDimitry Andric default: 10640b57cec5SDimitry Andric result.AppendError( 10650b57cec5SDimitry Andric "result size larger than 8 bytes. pass a string instead"); 10665f757f3fSDimitry Andric return; 10670b57cec5SDimitry Andric } 10680b57cec5SDimitry Andric } else { 10690b57cec5SDimitry Andric result.AppendError( 10700b57cec5SDimitry Andric "expression evaluation failed. pass a string instead"); 10715f757f3fSDimitry Andric return; 10720b57cec5SDimitry Andric } 10730b57cec5SDimitry Andric } else { 10740b57cec5SDimitry Andric result.AppendError( 10750b57cec5SDimitry Andric "please pass either a block of text, or an expression to evaluate."); 10765f757f3fSDimitry Andric return; 10770b57cec5SDimitry Andric } 10780b57cec5SDimitry Andric 10790b57cec5SDimitry Andric size_t count = m_memory_options.m_count.GetCurrentValue(); 10800b57cec5SDimitry Andric found_location = low_addr; 10810b57cec5SDimitry Andric bool ever_found = false; 10820b57cec5SDimitry Andric while (count) { 1083*0fca6ea1SDimitry Andric found_location = process->FindInMemory( 1084*0fca6ea1SDimitry Andric found_location, high_addr, buffer.GetBytes(), buffer.GetByteSize()); 10850b57cec5SDimitry Andric if (found_location == LLDB_INVALID_ADDRESS) { 10860b57cec5SDimitry Andric if (!ever_found) { 10870b57cec5SDimitry Andric result.AppendMessage("data not found within the range.\n"); 10880b57cec5SDimitry Andric result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); 10890b57cec5SDimitry Andric } else 10900b57cec5SDimitry Andric result.AppendMessage("no more matches within the range.\n"); 10910b57cec5SDimitry Andric break; 10920b57cec5SDimitry Andric } 10930b57cec5SDimitry Andric result.AppendMessageWithFormat("data found at location: 0x%" PRIx64 "\n", 10940b57cec5SDimitry Andric found_location); 10950b57cec5SDimitry Andric 10960b57cec5SDimitry Andric DataBufferHeap dumpbuffer(32, 0); 10970b57cec5SDimitry Andric process->ReadMemory( 10980b57cec5SDimitry Andric found_location + m_memory_options.m_offset.GetCurrentValue(), 10990b57cec5SDimitry Andric dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), error); 11000b57cec5SDimitry Andric if (!error.Fail()) { 11010b57cec5SDimitry Andric DataExtractor data(dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), 11020b57cec5SDimitry Andric process->GetByteOrder(), 11030b57cec5SDimitry Andric process->GetAddressByteSize()); 11040b57cec5SDimitry Andric DumpDataExtractor( 11050b57cec5SDimitry Andric data, &result.GetOutputStream(), 0, lldb::eFormatBytesWithASCII, 1, 11060b57cec5SDimitry Andric dumpbuffer.GetByteSize(), 16, 110781ad6265SDimitry Andric found_location + m_memory_options.m_offset.GetCurrentValue(), 0, 0, 110881ad6265SDimitry Andric m_exe_ctx.GetBestExecutionContextScope(), 110981ad6265SDimitry Andric m_memory_tag_options.GetShowTags().GetCurrentValue()); 11100b57cec5SDimitry Andric result.GetOutputStream().EOL(); 11110b57cec5SDimitry Andric } 11120b57cec5SDimitry Andric 11130b57cec5SDimitry Andric --count; 11140b57cec5SDimitry Andric found_location++; 11150b57cec5SDimitry Andric ever_found = true; 11160b57cec5SDimitry Andric } 11170b57cec5SDimitry Andric 11180b57cec5SDimitry Andric result.SetStatus(lldb::eReturnStatusSuccessFinishResult); 11190b57cec5SDimitry Andric } 11200b57cec5SDimitry Andric 11210b57cec5SDimitry Andric OptionGroupOptions m_option_group; 11220b57cec5SDimitry Andric OptionGroupFindMemory m_memory_options; 112381ad6265SDimitry Andric OptionGroupMemoryTag m_memory_tag_options; 11240b57cec5SDimitry Andric }; 11250b57cec5SDimitry Andric 11269dba64beSDimitry Andric #define LLDB_OPTIONS_memory_write 11279dba64beSDimitry Andric #include "CommandOptions.inc" 11280b57cec5SDimitry Andric 11290b57cec5SDimitry Andric // Write memory to the inferior process 11300b57cec5SDimitry Andric class CommandObjectMemoryWrite : public CommandObjectParsed { 11310b57cec5SDimitry Andric public: 11320b57cec5SDimitry Andric class OptionGroupWriteMemory : public OptionGroup { 11330b57cec5SDimitry Andric public: 113481ad6265SDimitry Andric OptionGroupWriteMemory() = default; 11350b57cec5SDimitry Andric 11360b57cec5SDimitry Andric ~OptionGroupWriteMemory() override = default; 11370b57cec5SDimitry Andric 11380b57cec5SDimitry Andric llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1139bdd1243dSDimitry Andric return llvm::ArrayRef(g_memory_write_options); 11400b57cec5SDimitry Andric } 11410b57cec5SDimitry Andric 11420b57cec5SDimitry Andric Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, 11430b57cec5SDimitry Andric ExecutionContext *execution_context) override { 11440b57cec5SDimitry Andric Status error; 11459dba64beSDimitry Andric const int short_option = g_memory_write_options[option_idx].short_option; 11460b57cec5SDimitry Andric 11470b57cec5SDimitry Andric switch (short_option) { 11480b57cec5SDimitry Andric case 'i': 11490b57cec5SDimitry Andric m_infile.SetFile(option_value, FileSpec::Style::native); 11500b57cec5SDimitry Andric FileSystem::Instance().Resolve(m_infile); 11510b57cec5SDimitry Andric if (!FileSystem::Instance().Exists(m_infile)) { 11520b57cec5SDimitry Andric m_infile.Clear(); 11530b57cec5SDimitry Andric error.SetErrorStringWithFormat("input file does not exist: '%s'", 11540b57cec5SDimitry Andric option_value.str().c_str()); 11550b57cec5SDimitry Andric } 11560b57cec5SDimitry Andric break; 11570b57cec5SDimitry Andric 11580b57cec5SDimitry Andric case 'o': { 11590b57cec5SDimitry Andric if (option_value.getAsInteger(0, m_infile_offset)) { 11600b57cec5SDimitry Andric m_infile_offset = 0; 11610b57cec5SDimitry Andric error.SetErrorStringWithFormat("invalid offset string '%s'", 11620b57cec5SDimitry Andric option_value.str().c_str()); 11630b57cec5SDimitry Andric } 11640b57cec5SDimitry Andric } break; 11650b57cec5SDimitry Andric 11660b57cec5SDimitry Andric default: 11679dba64beSDimitry Andric llvm_unreachable("Unimplemented option"); 11680b57cec5SDimitry Andric } 11690b57cec5SDimitry Andric return error; 11700b57cec5SDimitry Andric } 11710b57cec5SDimitry Andric 11720b57cec5SDimitry Andric void OptionParsingStarting(ExecutionContext *execution_context) override { 11730b57cec5SDimitry Andric m_infile.Clear(); 11740b57cec5SDimitry Andric m_infile_offset = 0; 11750b57cec5SDimitry Andric } 11760b57cec5SDimitry Andric 11770b57cec5SDimitry Andric FileSpec m_infile; 11780b57cec5SDimitry Andric off_t m_infile_offset; 11790b57cec5SDimitry Andric }; 11800b57cec5SDimitry Andric 11810b57cec5SDimitry Andric CommandObjectMemoryWrite(CommandInterpreter &interpreter) 11820b57cec5SDimitry Andric : CommandObjectParsed( 11830b57cec5SDimitry Andric interpreter, "memory write", 11840b57cec5SDimitry Andric "Write to the memory of the current target process.", nullptr, 11850b57cec5SDimitry Andric eCommandRequiresProcess | eCommandProcessMustBeLaunched), 11864824e7fdSDimitry Andric m_format_options( 11874824e7fdSDimitry Andric eFormatBytes, 1, UINT64_MAX, 11884824e7fdSDimitry Andric {std::make_tuple( 11894824e7fdSDimitry Andric eArgTypeFormat, 11904824e7fdSDimitry Andric "The format to use for each of the value to be written."), 119104eeddc0SDimitry Andric std::make_tuple(eArgTypeByteSize, 119204eeddc0SDimitry Andric "The size in bytes to write from input file or " 119304eeddc0SDimitry Andric "each value.")}) { 11940b57cec5SDimitry Andric CommandArgumentEntry arg1; 11950b57cec5SDimitry Andric CommandArgumentEntry arg2; 11960b57cec5SDimitry Andric CommandArgumentData addr_arg; 11970b57cec5SDimitry Andric CommandArgumentData value_arg; 11980b57cec5SDimitry Andric 11990b57cec5SDimitry Andric // Define the first (and only) variant of this arg. 12000b57cec5SDimitry Andric addr_arg.arg_type = eArgTypeAddress; 12010b57cec5SDimitry Andric addr_arg.arg_repetition = eArgRepeatPlain; 12020b57cec5SDimitry Andric 12030b57cec5SDimitry Andric // There is only one variant this argument could be; put it into the 12040b57cec5SDimitry Andric // argument entry. 12050b57cec5SDimitry Andric arg1.push_back(addr_arg); 12060b57cec5SDimitry Andric 12070b57cec5SDimitry Andric // Define the first (and only) variant of this arg. 12080b57cec5SDimitry Andric value_arg.arg_type = eArgTypeValue; 12090b57cec5SDimitry Andric value_arg.arg_repetition = eArgRepeatPlus; 12104824e7fdSDimitry Andric value_arg.arg_opt_set_association = LLDB_OPT_SET_1; 12110b57cec5SDimitry Andric 12120b57cec5SDimitry Andric // There is only one variant this argument could be; put it into the 12130b57cec5SDimitry Andric // argument entry. 12140b57cec5SDimitry Andric arg2.push_back(value_arg); 12150b57cec5SDimitry Andric 12160b57cec5SDimitry Andric // Push the data for the first argument into the m_arguments vector. 12170b57cec5SDimitry Andric m_arguments.push_back(arg1); 12180b57cec5SDimitry Andric m_arguments.push_back(arg2); 12190b57cec5SDimitry Andric 12200b57cec5SDimitry Andric m_option_group.Append(&m_format_options, 12210b57cec5SDimitry Andric OptionGroupFormat::OPTION_GROUP_FORMAT, 12220b57cec5SDimitry Andric LLDB_OPT_SET_1); 12230b57cec5SDimitry Andric m_option_group.Append(&m_format_options, 12240b57cec5SDimitry Andric OptionGroupFormat::OPTION_GROUP_SIZE, 12250b57cec5SDimitry Andric LLDB_OPT_SET_1 | LLDB_OPT_SET_2); 12260b57cec5SDimitry Andric m_option_group.Append(&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2); 12270b57cec5SDimitry Andric m_option_group.Finalize(); 12280b57cec5SDimitry Andric } 12290b57cec5SDimitry Andric 12300b57cec5SDimitry Andric ~CommandObjectMemoryWrite() override = default; 12310b57cec5SDimitry Andric 12320b57cec5SDimitry Andric Options *GetOptions() override { return &m_option_group; } 12330b57cec5SDimitry Andric 12340b57cec5SDimitry Andric protected: 12355f757f3fSDimitry Andric void DoExecute(Args &command, CommandReturnObject &result) override { 12360b57cec5SDimitry Andric // No need to check "process" for validity as eCommandRequiresProcess 12370b57cec5SDimitry Andric // ensures it is valid 12380b57cec5SDimitry Andric Process *process = m_exe_ctx.GetProcessPtr(); 12390b57cec5SDimitry Andric 12400b57cec5SDimitry Andric const size_t argc = command.GetArgumentCount(); 12410b57cec5SDimitry Andric 12420b57cec5SDimitry Andric if (m_memory_options.m_infile) { 12430b57cec5SDimitry Andric if (argc < 1) { 12440b57cec5SDimitry Andric result.AppendErrorWithFormat( 12450b57cec5SDimitry Andric "%s takes a destination address when writing file contents.\n", 12460b57cec5SDimitry Andric m_cmd_name.c_str()); 12475f757f3fSDimitry Andric return; 12480b57cec5SDimitry Andric } 12494824e7fdSDimitry Andric if (argc > 1) { 12504824e7fdSDimitry Andric result.AppendErrorWithFormat( 12514824e7fdSDimitry Andric "%s takes only a destination address when writing file contents.\n", 12524824e7fdSDimitry Andric m_cmd_name.c_str()); 12535f757f3fSDimitry Andric return; 12544824e7fdSDimitry Andric } 12550b57cec5SDimitry Andric } else if (argc < 2) { 12560b57cec5SDimitry Andric result.AppendErrorWithFormat( 12570b57cec5SDimitry Andric "%s takes a destination address and at least one value.\n", 12580b57cec5SDimitry Andric m_cmd_name.c_str()); 12595f757f3fSDimitry Andric return; 12600b57cec5SDimitry Andric } 12610b57cec5SDimitry Andric 12620b57cec5SDimitry Andric StreamString buffer( 12630b57cec5SDimitry Andric Stream::eBinary, 12640b57cec5SDimitry Andric process->GetTarget().GetArchitecture().GetAddressByteSize(), 12650b57cec5SDimitry Andric process->GetTarget().GetArchitecture().GetByteOrder()); 12660b57cec5SDimitry Andric 12670b57cec5SDimitry Andric OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue(); 12680b57cec5SDimitry Andric size_t item_byte_size = byte_size_value.GetCurrentValue(); 12690b57cec5SDimitry Andric 12700b57cec5SDimitry Andric Status error; 12710b57cec5SDimitry Andric lldb::addr_t addr = OptionArgParser::ToAddress( 12729dba64beSDimitry Andric &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); 12730b57cec5SDimitry Andric 12740b57cec5SDimitry Andric if (addr == LLDB_INVALID_ADDRESS) { 12750b57cec5SDimitry Andric result.AppendError("invalid address expression\n"); 12760b57cec5SDimitry Andric result.AppendError(error.AsCString()); 12775f757f3fSDimitry Andric return; 12780b57cec5SDimitry Andric } 12790b57cec5SDimitry Andric 12800b57cec5SDimitry Andric if (m_memory_options.m_infile) { 12810b57cec5SDimitry Andric size_t length = SIZE_MAX; 12820b57cec5SDimitry Andric if (item_byte_size > 1) 12830b57cec5SDimitry Andric length = item_byte_size; 12840b57cec5SDimitry Andric auto data_sp = FileSystem::Instance().CreateDataBuffer( 12850b57cec5SDimitry Andric m_memory_options.m_infile.GetPath(), length, 12860b57cec5SDimitry Andric m_memory_options.m_infile_offset); 12870b57cec5SDimitry Andric if (data_sp) { 12880b57cec5SDimitry Andric length = data_sp->GetByteSize(); 12890b57cec5SDimitry Andric if (length > 0) { 12900b57cec5SDimitry Andric Status error; 12910b57cec5SDimitry Andric size_t bytes_written = 12920b57cec5SDimitry Andric process->WriteMemory(addr, data_sp->GetBytes(), length, error); 12930b57cec5SDimitry Andric 12940b57cec5SDimitry Andric if (bytes_written == length) { 12950b57cec5SDimitry Andric // All bytes written 12960b57cec5SDimitry Andric result.GetOutputStream().Printf( 12970b57cec5SDimitry Andric "%" PRIu64 " bytes were written to 0x%" PRIx64 "\n", 12980b57cec5SDimitry Andric (uint64_t)bytes_written, addr); 12990b57cec5SDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 13000b57cec5SDimitry Andric } else if (bytes_written > 0) { 13010b57cec5SDimitry Andric // Some byte written 13020b57cec5SDimitry Andric result.GetOutputStream().Printf( 13030b57cec5SDimitry Andric "%" PRIu64 " bytes of %" PRIu64 13040b57cec5SDimitry Andric " requested were written to 0x%" PRIx64 "\n", 13050b57cec5SDimitry Andric (uint64_t)bytes_written, (uint64_t)length, addr); 13060b57cec5SDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 13070b57cec5SDimitry Andric } else { 13080b57cec5SDimitry Andric result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 13090b57cec5SDimitry Andric " failed: %s.\n", 13100b57cec5SDimitry Andric addr, error.AsCString()); 13110b57cec5SDimitry Andric } 13120b57cec5SDimitry Andric } 13130b57cec5SDimitry Andric } else { 13140b57cec5SDimitry Andric result.AppendErrorWithFormat("Unable to read contents of file.\n"); 13150b57cec5SDimitry Andric } 13165f757f3fSDimitry Andric return; 13170b57cec5SDimitry Andric } else if (item_byte_size == 0) { 13180b57cec5SDimitry Andric if (m_format_options.GetFormat() == eFormatPointer) 13190b57cec5SDimitry Andric item_byte_size = buffer.GetAddressByteSize(); 13200b57cec5SDimitry Andric else 13210b57cec5SDimitry Andric item_byte_size = 1; 13220b57cec5SDimitry Andric } 13230b57cec5SDimitry Andric 13240b57cec5SDimitry Andric command.Shift(); // shift off the address argument 13250b57cec5SDimitry Andric uint64_t uval64; 13260b57cec5SDimitry Andric int64_t sval64; 13270b57cec5SDimitry Andric bool success = false; 13280b57cec5SDimitry Andric for (auto &entry : command) { 13290b57cec5SDimitry Andric switch (m_format_options.GetFormat()) { 13300b57cec5SDimitry Andric case kNumFormats: 13310b57cec5SDimitry Andric case eFormatFloat: // TODO: add support for floats soon 13320b57cec5SDimitry Andric case eFormatCharPrintable: 13330b57cec5SDimitry Andric case eFormatBytesWithASCII: 13340b57cec5SDimitry Andric case eFormatComplex: 13350b57cec5SDimitry Andric case eFormatEnum: 13369dba64beSDimitry Andric case eFormatUnicode8: 13370b57cec5SDimitry Andric case eFormatUnicode16: 13380b57cec5SDimitry Andric case eFormatUnicode32: 13390b57cec5SDimitry Andric case eFormatVectorOfChar: 13400b57cec5SDimitry Andric case eFormatVectorOfSInt8: 13410b57cec5SDimitry Andric case eFormatVectorOfUInt8: 13420b57cec5SDimitry Andric case eFormatVectorOfSInt16: 13430b57cec5SDimitry Andric case eFormatVectorOfUInt16: 13440b57cec5SDimitry Andric case eFormatVectorOfSInt32: 13450b57cec5SDimitry Andric case eFormatVectorOfUInt32: 13460b57cec5SDimitry Andric case eFormatVectorOfSInt64: 13470b57cec5SDimitry Andric case eFormatVectorOfUInt64: 13480b57cec5SDimitry Andric case eFormatVectorOfFloat16: 13490b57cec5SDimitry Andric case eFormatVectorOfFloat32: 13500b57cec5SDimitry Andric case eFormatVectorOfFloat64: 13510b57cec5SDimitry Andric case eFormatVectorOfUInt128: 13520b57cec5SDimitry Andric case eFormatOSType: 13530b57cec5SDimitry Andric case eFormatComplexInteger: 13540b57cec5SDimitry Andric case eFormatAddressInfo: 13550b57cec5SDimitry Andric case eFormatHexFloat: 13560b57cec5SDimitry Andric case eFormatInstruction: 13570b57cec5SDimitry Andric case eFormatVoid: 13580b57cec5SDimitry Andric result.AppendError("unsupported format for writing memory"); 13595f757f3fSDimitry Andric return; 13600b57cec5SDimitry Andric 13610b57cec5SDimitry Andric case eFormatDefault: 13620b57cec5SDimitry Andric case eFormatBytes: 13630b57cec5SDimitry Andric case eFormatHex: 13640b57cec5SDimitry Andric case eFormatHexUppercase: 1365480093f4SDimitry Andric case eFormatPointer: { 13660b57cec5SDimitry Andric // Decode hex bytes 13670b57cec5SDimitry Andric // Be careful, getAsInteger with a radix of 16 rejects "0xab" so we 13680b57cec5SDimitry Andric // have to special case that: 13690b57cec5SDimitry Andric bool success = false; 13705f757f3fSDimitry Andric if (entry.ref().starts_with("0x")) 13719dba64beSDimitry Andric success = !entry.ref().getAsInteger(0, uval64); 13720b57cec5SDimitry Andric if (!success) 13739dba64beSDimitry Andric success = !entry.ref().getAsInteger(16, uval64); 13740b57cec5SDimitry Andric if (!success) { 13750b57cec5SDimitry Andric result.AppendErrorWithFormat( 13760b57cec5SDimitry Andric "'%s' is not a valid hex string value.\n", entry.c_str()); 13775f757f3fSDimitry Andric return; 1378e8d8bef9SDimitry Andric } else if (!llvm::isUIntN(item_byte_size * 8, uval64)) { 13790b57cec5SDimitry Andric result.AppendErrorWithFormat("Value 0x%" PRIx64 13800b57cec5SDimitry Andric " is too large to fit in a %" PRIu64 13810b57cec5SDimitry Andric " byte unsigned integer value.\n", 13820b57cec5SDimitry Andric uval64, (uint64_t)item_byte_size); 13835f757f3fSDimitry Andric return; 13840b57cec5SDimitry Andric } 13850b57cec5SDimitry Andric buffer.PutMaxHex64(uval64, item_byte_size); 13860b57cec5SDimitry Andric break; 13870b57cec5SDimitry Andric } 13880b57cec5SDimitry Andric case eFormatBoolean: 13899dba64beSDimitry Andric uval64 = OptionArgParser::ToBoolean(entry.ref(), false, &success); 13900b57cec5SDimitry Andric if (!success) { 13910b57cec5SDimitry Andric result.AppendErrorWithFormat( 13920b57cec5SDimitry Andric "'%s' is not a valid boolean string value.\n", entry.c_str()); 13935f757f3fSDimitry Andric return; 13940b57cec5SDimitry Andric } 13950b57cec5SDimitry Andric buffer.PutMaxHex64(uval64, item_byte_size); 13960b57cec5SDimitry Andric break; 13970b57cec5SDimitry Andric 13980b57cec5SDimitry Andric case eFormatBinary: 13999dba64beSDimitry Andric if (entry.ref().getAsInteger(2, uval64)) { 14000b57cec5SDimitry Andric result.AppendErrorWithFormat( 14010b57cec5SDimitry Andric "'%s' is not a valid binary string value.\n", entry.c_str()); 14025f757f3fSDimitry Andric return; 1403e8d8bef9SDimitry Andric } else if (!llvm::isUIntN(item_byte_size * 8, uval64)) { 14040b57cec5SDimitry Andric result.AppendErrorWithFormat("Value 0x%" PRIx64 14050b57cec5SDimitry Andric " is too large to fit in a %" PRIu64 14060b57cec5SDimitry Andric " byte unsigned integer value.\n", 14070b57cec5SDimitry Andric uval64, (uint64_t)item_byte_size); 14085f757f3fSDimitry Andric return; 14090b57cec5SDimitry Andric } 14100b57cec5SDimitry Andric buffer.PutMaxHex64(uval64, item_byte_size); 14110b57cec5SDimitry Andric break; 14120b57cec5SDimitry Andric 14130b57cec5SDimitry Andric case eFormatCharArray: 14140b57cec5SDimitry Andric case eFormatChar: 14150b57cec5SDimitry Andric case eFormatCString: { 14169dba64beSDimitry Andric if (entry.ref().empty()) 14170b57cec5SDimitry Andric break; 14180b57cec5SDimitry Andric 14199dba64beSDimitry Andric size_t len = entry.ref().size(); 14200b57cec5SDimitry Andric // Include the NULL for C strings... 14210b57cec5SDimitry Andric if (m_format_options.GetFormat() == eFormatCString) 14220b57cec5SDimitry Andric ++len; 14230b57cec5SDimitry Andric Status error; 14240b57cec5SDimitry Andric if (process->WriteMemory(addr, entry.c_str(), len, error) == len) { 14250b57cec5SDimitry Andric addr += len; 14260b57cec5SDimitry Andric } else { 14270b57cec5SDimitry Andric result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 14280b57cec5SDimitry Andric " failed: %s.\n", 14290b57cec5SDimitry Andric addr, error.AsCString()); 14305f757f3fSDimitry Andric return; 14310b57cec5SDimitry Andric } 14320b57cec5SDimitry Andric break; 14330b57cec5SDimitry Andric } 14340b57cec5SDimitry Andric case eFormatDecimal: 14359dba64beSDimitry Andric if (entry.ref().getAsInteger(0, sval64)) { 14360b57cec5SDimitry Andric result.AppendErrorWithFormat( 14370b57cec5SDimitry Andric "'%s' is not a valid signed decimal value.\n", entry.c_str()); 14385f757f3fSDimitry Andric return; 1439e8d8bef9SDimitry Andric } else if (!llvm::isIntN(item_byte_size * 8, sval64)) { 14400b57cec5SDimitry Andric result.AppendErrorWithFormat( 14410b57cec5SDimitry Andric "Value %" PRIi64 " is too large or small to fit in a %" PRIu64 14420b57cec5SDimitry Andric " byte signed integer value.\n", 14430b57cec5SDimitry Andric sval64, (uint64_t)item_byte_size); 14445f757f3fSDimitry Andric return; 14450b57cec5SDimitry Andric } 14460b57cec5SDimitry Andric buffer.PutMaxHex64(sval64, item_byte_size); 14470b57cec5SDimitry Andric break; 14480b57cec5SDimitry Andric 14490b57cec5SDimitry Andric case eFormatUnsigned: 14500b57cec5SDimitry Andric 1451fe6060f1SDimitry Andric if (entry.ref().getAsInteger(0, uval64)) { 14520b57cec5SDimitry Andric result.AppendErrorWithFormat( 14530b57cec5SDimitry Andric "'%s' is not a valid unsigned decimal string value.\n", 14540b57cec5SDimitry Andric entry.c_str()); 14555f757f3fSDimitry Andric return; 1456e8d8bef9SDimitry Andric } else if (!llvm::isUIntN(item_byte_size * 8, uval64)) { 14570b57cec5SDimitry Andric result.AppendErrorWithFormat("Value %" PRIu64 14580b57cec5SDimitry Andric " is too large to fit in a %" PRIu64 14590b57cec5SDimitry Andric " byte unsigned integer value.\n", 14600b57cec5SDimitry Andric uval64, (uint64_t)item_byte_size); 14615f757f3fSDimitry Andric return; 14620b57cec5SDimitry Andric } 14630b57cec5SDimitry Andric buffer.PutMaxHex64(uval64, item_byte_size); 14640b57cec5SDimitry Andric break; 14650b57cec5SDimitry Andric 14660b57cec5SDimitry Andric case eFormatOctal: 14679dba64beSDimitry Andric if (entry.ref().getAsInteger(8, uval64)) { 14680b57cec5SDimitry Andric result.AppendErrorWithFormat( 14690b57cec5SDimitry Andric "'%s' is not a valid octal string value.\n", entry.c_str()); 14705f757f3fSDimitry Andric return; 1471e8d8bef9SDimitry Andric } else if (!llvm::isUIntN(item_byte_size * 8, uval64)) { 14720b57cec5SDimitry Andric result.AppendErrorWithFormat("Value %" PRIo64 14730b57cec5SDimitry Andric " is too large to fit in a %" PRIu64 14740b57cec5SDimitry Andric " byte unsigned integer value.\n", 14750b57cec5SDimitry Andric uval64, (uint64_t)item_byte_size); 14765f757f3fSDimitry Andric return; 14770b57cec5SDimitry Andric } 14780b57cec5SDimitry Andric buffer.PutMaxHex64(uval64, item_byte_size); 14790b57cec5SDimitry Andric break; 14800b57cec5SDimitry Andric } 14810b57cec5SDimitry Andric } 14820b57cec5SDimitry Andric 14830b57cec5SDimitry Andric if (!buffer.GetString().empty()) { 14840b57cec5SDimitry Andric Status error; 14855f757f3fSDimitry Andric const char *buffer_data = buffer.GetString().data(); 14865f757f3fSDimitry Andric const size_t buffer_size = buffer.GetString().size(); 14875f757f3fSDimitry Andric const size_t write_size = 14885f757f3fSDimitry Andric process->WriteMemory(addr, buffer_data, buffer_size, error); 14895f757f3fSDimitry Andric 14905f757f3fSDimitry Andric if (write_size != buffer_size) { 14910b57cec5SDimitry Andric result.AppendErrorWithFormat("Memory write to 0x%" PRIx64 14920b57cec5SDimitry Andric " failed: %s.\n", 14930b57cec5SDimitry Andric addr, error.AsCString()); 14945f757f3fSDimitry Andric return; 14950b57cec5SDimitry Andric } 14960b57cec5SDimitry Andric } 14970b57cec5SDimitry Andric } 14980b57cec5SDimitry Andric 14990b57cec5SDimitry Andric OptionGroupOptions m_option_group; 15000b57cec5SDimitry Andric OptionGroupFormat m_format_options; 15010b57cec5SDimitry Andric OptionGroupWriteMemory m_memory_options; 15020b57cec5SDimitry Andric }; 15030b57cec5SDimitry Andric 15040b57cec5SDimitry Andric // Get malloc/free history of a memory address. 15050b57cec5SDimitry Andric class CommandObjectMemoryHistory : public CommandObjectParsed { 15060b57cec5SDimitry Andric public: 15070b57cec5SDimitry Andric CommandObjectMemoryHistory(CommandInterpreter &interpreter) 1508480093f4SDimitry Andric : CommandObjectParsed(interpreter, "memory history", 1509480093f4SDimitry Andric "Print recorded stack traces for " 15100b57cec5SDimitry Andric "allocation/deallocation events " 15110b57cec5SDimitry Andric "associated with an address.", 15120b57cec5SDimitry Andric nullptr, 15130b57cec5SDimitry Andric eCommandRequiresTarget | eCommandRequiresProcess | 1514480093f4SDimitry Andric eCommandProcessMustBePaused | 1515480093f4SDimitry Andric eCommandProcessMustBeLaunched) { 15160b57cec5SDimitry Andric CommandArgumentEntry arg1; 15170b57cec5SDimitry Andric CommandArgumentData addr_arg; 15180b57cec5SDimitry Andric 15190b57cec5SDimitry Andric // Define the first (and only) variant of this arg. 15200b57cec5SDimitry Andric addr_arg.arg_type = eArgTypeAddress; 15210b57cec5SDimitry Andric addr_arg.arg_repetition = eArgRepeatPlain; 15220b57cec5SDimitry Andric 15230b57cec5SDimitry Andric // There is only one variant this argument could be; put it into the 15240b57cec5SDimitry Andric // argument entry. 15250b57cec5SDimitry Andric arg1.push_back(addr_arg); 15260b57cec5SDimitry Andric 15270b57cec5SDimitry Andric // Push the data for the first argument into the m_arguments vector. 15280b57cec5SDimitry Andric m_arguments.push_back(arg1); 15290b57cec5SDimitry Andric } 15300b57cec5SDimitry Andric 15310b57cec5SDimitry Andric ~CommandObjectMemoryHistory() override = default; 15320b57cec5SDimitry Andric 1533bdd1243dSDimitry Andric std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, 15340b57cec5SDimitry Andric uint32_t index) override { 153581ad6265SDimitry Andric return m_cmd_name; 15360b57cec5SDimitry Andric } 15370b57cec5SDimitry Andric 15380b57cec5SDimitry Andric protected: 15395f757f3fSDimitry Andric void DoExecute(Args &command, CommandReturnObject &result) override { 15400b57cec5SDimitry Andric const size_t argc = command.GetArgumentCount(); 15410b57cec5SDimitry Andric 15420b57cec5SDimitry Andric if (argc == 0 || argc > 1) { 15430b57cec5SDimitry Andric result.AppendErrorWithFormat("%s takes an address expression", 15440b57cec5SDimitry Andric m_cmd_name.c_str()); 15455f757f3fSDimitry Andric return; 15460b57cec5SDimitry Andric } 15470b57cec5SDimitry Andric 15480b57cec5SDimitry Andric Status error; 15490b57cec5SDimitry Andric lldb::addr_t addr = OptionArgParser::ToAddress( 15509dba64beSDimitry Andric &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); 15510b57cec5SDimitry Andric 15520b57cec5SDimitry Andric if (addr == LLDB_INVALID_ADDRESS) { 15530b57cec5SDimitry Andric result.AppendError("invalid address expression"); 15540b57cec5SDimitry Andric result.AppendError(error.AsCString()); 15555f757f3fSDimitry Andric return; 15560b57cec5SDimitry Andric } 15570b57cec5SDimitry Andric 15580b57cec5SDimitry Andric Stream *output_stream = &result.GetOutputStream(); 15590b57cec5SDimitry Andric 15600b57cec5SDimitry Andric const ProcessSP &process_sp = m_exe_ctx.GetProcessSP(); 15610b57cec5SDimitry Andric const MemoryHistorySP &memory_history = 15620b57cec5SDimitry Andric MemoryHistory::FindPlugin(process_sp); 15630b57cec5SDimitry Andric 15640b57cec5SDimitry Andric if (!memory_history) { 15650b57cec5SDimitry Andric result.AppendError("no available memory history provider"); 15665f757f3fSDimitry Andric return; 15670b57cec5SDimitry Andric } 15680b57cec5SDimitry Andric 15690b57cec5SDimitry Andric HistoryThreads thread_list = memory_history->GetHistoryThreads(addr); 15700b57cec5SDimitry Andric 15710b57cec5SDimitry Andric const bool stop_format = false; 15720b57cec5SDimitry Andric for (auto thread : thread_list) { 15730b57cec5SDimitry Andric thread->GetStatus(*output_stream, 0, UINT32_MAX, 0, stop_format); 15740b57cec5SDimitry Andric } 15750b57cec5SDimitry Andric 15760b57cec5SDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 15770b57cec5SDimitry Andric } 15780b57cec5SDimitry Andric }; 15790b57cec5SDimitry Andric 15800b57cec5SDimitry Andric // CommandObjectMemoryRegion 15810b57cec5SDimitry Andric #pragma mark CommandObjectMemoryRegion 15820b57cec5SDimitry Andric 158381ad6265SDimitry Andric #define LLDB_OPTIONS_memory_region 158481ad6265SDimitry Andric #include "CommandOptions.inc" 158581ad6265SDimitry Andric 15860b57cec5SDimitry Andric class CommandObjectMemoryRegion : public CommandObjectParsed { 15870b57cec5SDimitry Andric public: 158881ad6265SDimitry Andric class OptionGroupMemoryRegion : public OptionGroup { 158981ad6265SDimitry Andric public: 1590972a253aSDimitry Andric OptionGroupMemoryRegion() : m_all(false, false) {} 159181ad6265SDimitry Andric 159281ad6265SDimitry Andric ~OptionGroupMemoryRegion() override = default; 159381ad6265SDimitry Andric 159481ad6265SDimitry Andric llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 1595bdd1243dSDimitry Andric return llvm::ArrayRef(g_memory_region_options); 159681ad6265SDimitry Andric } 159781ad6265SDimitry Andric 159881ad6265SDimitry Andric Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, 159981ad6265SDimitry Andric ExecutionContext *execution_context) override { 160081ad6265SDimitry Andric Status status; 160181ad6265SDimitry Andric const int short_option = g_memory_region_options[option_idx].short_option; 160281ad6265SDimitry Andric 160381ad6265SDimitry Andric switch (short_option) { 160481ad6265SDimitry Andric case 'a': 160581ad6265SDimitry Andric m_all.SetCurrentValue(true); 160681ad6265SDimitry Andric m_all.SetOptionWasSet(); 160781ad6265SDimitry Andric break; 160881ad6265SDimitry Andric default: 160981ad6265SDimitry Andric llvm_unreachable("Unimplemented option"); 161081ad6265SDimitry Andric } 161181ad6265SDimitry Andric 161281ad6265SDimitry Andric return status; 161381ad6265SDimitry Andric } 161481ad6265SDimitry Andric 161581ad6265SDimitry Andric void OptionParsingStarting(ExecutionContext *execution_context) override { 161681ad6265SDimitry Andric m_all.Clear(); 161781ad6265SDimitry Andric } 161881ad6265SDimitry Andric 161981ad6265SDimitry Andric OptionValueBoolean m_all; 162081ad6265SDimitry Andric }; 162181ad6265SDimitry Andric 16220b57cec5SDimitry Andric CommandObjectMemoryRegion(CommandInterpreter &interpreter) 16230b57cec5SDimitry Andric : CommandObjectParsed(interpreter, "memory region", 16240b57cec5SDimitry Andric "Get information on the memory region containing " 16250b57cec5SDimitry Andric "an address in the current target process.", 162681ad6265SDimitry Andric "memory region <address-expression> (or --all)", 16270b57cec5SDimitry Andric eCommandRequiresProcess | eCommandTryTargetAPILock | 162881ad6265SDimitry Andric eCommandProcessMustBeLaunched) { 162981ad6265SDimitry Andric // Address in option set 1. 163081ad6265SDimitry Andric m_arguments.push_back(CommandArgumentEntry{CommandArgumentData( 163181ad6265SDimitry Andric eArgTypeAddressOrExpression, eArgRepeatPlain, LLDB_OPT_SET_1)}); 163281ad6265SDimitry Andric // "--all" will go in option set 2. 163381ad6265SDimitry Andric m_option_group.Append(&m_memory_region_options); 163481ad6265SDimitry Andric m_option_group.Finalize(); 163581ad6265SDimitry Andric } 16360b57cec5SDimitry Andric 16370b57cec5SDimitry Andric ~CommandObjectMemoryRegion() override = default; 16380b57cec5SDimitry Andric 163981ad6265SDimitry Andric Options *GetOptions() override { return &m_option_group; } 164081ad6265SDimitry Andric 16410b57cec5SDimitry Andric protected: 164281ad6265SDimitry Andric void DumpRegion(CommandReturnObject &result, Target &target, 164381ad6265SDimitry Andric const MemoryRegionInfo &range_info, lldb::addr_t load_addr) { 164481ad6265SDimitry Andric lldb_private::Address addr; 164581ad6265SDimitry Andric ConstString section_name; 164681ad6265SDimitry Andric if (target.ResolveLoadAddress(load_addr, addr)) { 164781ad6265SDimitry Andric SectionSP section_sp(addr.GetSection()); 164881ad6265SDimitry Andric if (section_sp) { 164981ad6265SDimitry Andric // Got the top most section, not the deepest section 165081ad6265SDimitry Andric while (section_sp->GetParent()) 165181ad6265SDimitry Andric section_sp = section_sp->GetParent(); 165281ad6265SDimitry Andric section_name = section_sp->GetName(); 165381ad6265SDimitry Andric } 165481ad6265SDimitry Andric } 165581ad6265SDimitry Andric 165681ad6265SDimitry Andric ConstString name = range_info.GetName(); 165781ad6265SDimitry Andric result.AppendMessageWithFormatv( 165881ad6265SDimitry Andric "[{0:x16}-{1:x16}) {2:r}{3:w}{4:x}{5}{6}{7}{8}", 165981ad6265SDimitry Andric range_info.GetRange().GetRangeBase(), 166081ad6265SDimitry Andric range_info.GetRange().GetRangeEnd(), range_info.GetReadable(), 166181ad6265SDimitry Andric range_info.GetWritable(), range_info.GetExecutable(), name ? " " : "", 166281ad6265SDimitry Andric name, section_name ? " " : "", section_name); 166381ad6265SDimitry Andric MemoryRegionInfo::OptionalBool memory_tagged = range_info.GetMemoryTagged(); 166481ad6265SDimitry Andric if (memory_tagged == MemoryRegionInfo::OptionalBool::eYes) 166581ad6265SDimitry Andric result.AppendMessage("memory tagging: enabled"); 166681ad6265SDimitry Andric 1667bdd1243dSDimitry Andric const std::optional<std::vector<addr_t>> &dirty_page_list = 166881ad6265SDimitry Andric range_info.GetDirtyPageList(); 166981ad6265SDimitry Andric if (dirty_page_list) { 1670bdd1243dSDimitry Andric const size_t page_count = dirty_page_list->size(); 167181ad6265SDimitry Andric result.AppendMessageWithFormat( 167281ad6265SDimitry Andric "Modified memory (dirty) page list provided, %zu entries.\n", 167381ad6265SDimitry Andric page_count); 167481ad6265SDimitry Andric if (page_count > 0) { 167581ad6265SDimitry Andric bool print_comma = false; 167681ad6265SDimitry Andric result.AppendMessageWithFormat("Dirty pages: "); 167781ad6265SDimitry Andric for (size_t i = 0; i < page_count; i++) { 167881ad6265SDimitry Andric if (print_comma) 167981ad6265SDimitry Andric result.AppendMessageWithFormat(", "); 168081ad6265SDimitry Andric else 168181ad6265SDimitry Andric print_comma = true; 168281ad6265SDimitry Andric result.AppendMessageWithFormat("0x%" PRIx64, (*dirty_page_list)[i]); 168381ad6265SDimitry Andric } 168481ad6265SDimitry Andric result.AppendMessageWithFormat(".\n"); 168581ad6265SDimitry Andric } 168681ad6265SDimitry Andric } 168781ad6265SDimitry Andric } 168881ad6265SDimitry Andric 16895f757f3fSDimitry Andric void DoExecute(Args &command, CommandReturnObject &result) override { 16900b57cec5SDimitry Andric ProcessSP process_sp = m_exe_ctx.GetProcessSP(); 1691e8d8bef9SDimitry Andric if (!process_sp) { 1692e8d8bef9SDimitry Andric m_prev_end_addr = LLDB_INVALID_ADDRESS; 1693e8d8bef9SDimitry Andric result.AppendError("invalid process"); 16945f757f3fSDimitry Andric return; 1695e8d8bef9SDimitry Andric } 1696e8d8bef9SDimitry Andric 16970b57cec5SDimitry Andric Status error; 16980b57cec5SDimitry Andric lldb::addr_t load_addr = m_prev_end_addr; 16990b57cec5SDimitry Andric m_prev_end_addr = LLDB_INVALID_ADDRESS; 17000b57cec5SDimitry Andric 17010b57cec5SDimitry Andric const size_t argc = command.GetArgumentCount(); 1702d56accc7SDimitry Andric const lldb::ABISP &abi = process_sp->GetABI(); 1703e8d8bef9SDimitry Andric 1704e8d8bef9SDimitry Andric if (argc == 1) { 170581ad6265SDimitry Andric if (m_memory_region_options.m_all) { 170681ad6265SDimitry Andric result.AppendError( 170781ad6265SDimitry Andric "The \"--all\" option cannot be used when an address " 170881ad6265SDimitry Andric "argument is given"); 17095f757f3fSDimitry Andric return; 171081ad6265SDimitry Andric } 171181ad6265SDimitry Andric 17129dba64beSDimitry Andric auto load_addr_str = command[0].ref(); 17130b57cec5SDimitry Andric load_addr = OptionArgParser::ToAddress(&m_exe_ctx, load_addr_str, 17140b57cec5SDimitry Andric LLDB_INVALID_ADDRESS, &error); 17150b57cec5SDimitry Andric if (error.Fail() || load_addr == LLDB_INVALID_ADDRESS) { 1716e8d8bef9SDimitry Andric result.AppendErrorWithFormat("invalid address argument \"%s\": %s\n", 1717e8d8bef9SDimitry Andric command[0].c_str(), error.AsCString()); 17185f757f3fSDimitry Andric return; 17190b57cec5SDimitry Andric } 1720d56accc7SDimitry Andric } else if (argc > 1 || 1721d56accc7SDimitry Andric // When we're repeating the command, the previous end address is 1722d56accc7SDimitry Andric // used for load_addr. If that was 0xF...F then we must have 1723d56accc7SDimitry Andric // reached the end of memory. 172481ad6265SDimitry Andric (argc == 0 && !m_memory_region_options.m_all && 172581ad6265SDimitry Andric load_addr == LLDB_INVALID_ADDRESS) || 1726d56accc7SDimitry Andric // If the target has non-address bits (tags, limited virtual 1727d56accc7SDimitry Andric // address size, etc.), the end of mappable memory will be lower 1728d56accc7SDimitry Andric // than that. So if we find any non-address bit set, we must be 1729d56accc7SDimitry Andric // at the end of the mappable range. 173081ad6265SDimitry Andric (abi && (abi->FixAnyAddress(load_addr) != load_addr))) { 173181ad6265SDimitry Andric result.AppendErrorWithFormat( 173281ad6265SDimitry Andric "'%s' takes one argument or \"--all\" option:\nUsage: %s\n", 1733d56accc7SDimitry Andric m_cmd_name.c_str(), m_cmd_syntax.c_str()); 17345f757f3fSDimitry Andric return; 17350b57cec5SDimitry Andric } 17360b57cec5SDimitry Andric 17375f757f3fSDimitry Andric // It is important that we track the address used to request the region as 173881ad6265SDimitry Andric // this will give the correct section name in the case that regions overlap. 173981ad6265SDimitry Andric // On Windows we get mutliple regions that start at the same place but are 174081ad6265SDimitry Andric // different sizes and refer to different sections. 174181ad6265SDimitry Andric std::vector<std::pair<lldb_private::MemoryRegionInfo, lldb::addr_t>> 174281ad6265SDimitry Andric region_list; 174381ad6265SDimitry Andric if (m_memory_region_options.m_all) { 174481ad6265SDimitry Andric // We don't use GetMemoryRegions here because it doesn't include unmapped 174581ad6265SDimitry Andric // areas like repeating the command would. So instead, emulate doing that. 174681ad6265SDimitry Andric lldb::addr_t addr = 0; 174781ad6265SDimitry Andric while (error.Success() && addr != LLDB_INVALID_ADDRESS && 174881ad6265SDimitry Andric // When there are non-address bits the last range will not extend 174981ad6265SDimitry Andric // to LLDB_INVALID_ADDRESS but to the max virtual address. 175081ad6265SDimitry Andric // This prevents us looping forever if that is the case. 1751bdd1243dSDimitry Andric (!abi || (abi->FixAnyAddress(addr) == addr))) { 175281ad6265SDimitry Andric lldb_private::MemoryRegionInfo region_info; 175381ad6265SDimitry Andric error = process_sp->GetMemoryRegionInfo(addr, region_info); 175481ad6265SDimitry Andric 17550b57cec5SDimitry Andric if (error.Success()) { 175681ad6265SDimitry Andric region_list.push_back({region_info, addr}); 175781ad6265SDimitry Andric addr = region_info.GetRange().GetRangeEnd(); 17580b57cec5SDimitry Andric } 17590b57cec5SDimitry Andric } 176081ad6265SDimitry Andric } else { 176181ad6265SDimitry Andric lldb_private::MemoryRegionInfo region_info; 176281ad6265SDimitry Andric error = process_sp->GetMemoryRegionInfo(load_addr, region_info); 176381ad6265SDimitry Andric if (error.Success()) 176481ad6265SDimitry Andric region_list.push_back({region_info, load_addr}); 1765fe6060f1SDimitry Andric } 1766fe6060f1SDimitry Andric 176781ad6265SDimitry Andric if (error.Success()) { 176881ad6265SDimitry Andric for (std::pair<MemoryRegionInfo, addr_t> &range : region_list) { 176981ad6265SDimitry Andric DumpRegion(result, process_sp->GetTarget(), range.first, range.second); 177081ad6265SDimitry Andric m_prev_end_addr = range.first.GetRange().GetRangeEnd(); 177181ad6265SDimitry Andric } 177281ad6265SDimitry Andric 17730b57cec5SDimitry Andric result.SetStatus(eReturnStatusSuccessFinishResult); 17745f757f3fSDimitry Andric return; 1775e8d8bef9SDimitry Andric } 1776e8d8bef9SDimitry Andric 17770b57cec5SDimitry Andric result.AppendErrorWithFormat("%s\n", error.AsCString()); 17780b57cec5SDimitry Andric } 17790b57cec5SDimitry Andric 1780bdd1243dSDimitry Andric std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, 17810b57cec5SDimitry Andric uint32_t index) override { 17820b57cec5SDimitry Andric // If we repeat this command, repeat it without any arguments so we can 17830b57cec5SDimitry Andric // show the next memory range 178481ad6265SDimitry Andric return m_cmd_name; 17850b57cec5SDimitry Andric } 17860b57cec5SDimitry Andric 178781ad6265SDimitry Andric lldb::addr_t m_prev_end_addr = LLDB_INVALID_ADDRESS; 178881ad6265SDimitry Andric 178981ad6265SDimitry Andric OptionGroupOptions m_option_group; 179081ad6265SDimitry Andric OptionGroupMemoryRegion m_memory_region_options; 17910b57cec5SDimitry Andric }; 17920b57cec5SDimitry Andric 17930b57cec5SDimitry Andric // CommandObjectMemory 17940b57cec5SDimitry Andric 17950b57cec5SDimitry Andric CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter) 17960b57cec5SDimitry Andric : CommandObjectMultiword( 17970b57cec5SDimitry Andric interpreter, "memory", 17980b57cec5SDimitry Andric "Commands for operating on memory in the current target process.", 17990b57cec5SDimitry Andric "memory <subcommand> [<subcommand-options>]") { 18000b57cec5SDimitry Andric LoadSubCommand("find", 18010b57cec5SDimitry Andric CommandObjectSP(new CommandObjectMemoryFind(interpreter))); 18020b57cec5SDimitry Andric LoadSubCommand("read", 18030b57cec5SDimitry Andric CommandObjectSP(new CommandObjectMemoryRead(interpreter))); 18040b57cec5SDimitry Andric LoadSubCommand("write", 18050b57cec5SDimitry Andric CommandObjectSP(new CommandObjectMemoryWrite(interpreter))); 18060b57cec5SDimitry Andric LoadSubCommand("history", 18070b57cec5SDimitry Andric CommandObjectSP(new CommandObjectMemoryHistory(interpreter))); 18080b57cec5SDimitry Andric LoadSubCommand("region", 18090b57cec5SDimitry Andric CommandObjectSP(new CommandObjectMemoryRegion(interpreter))); 1810fe6060f1SDimitry Andric LoadSubCommand("tag", 1811fe6060f1SDimitry Andric CommandObjectSP(new CommandObjectMemoryTag(interpreter))); 18120b57cec5SDimitry Andric } 18130b57cec5SDimitry Andric 18140b57cec5SDimitry Andric CommandObjectMemory::~CommandObjectMemory() = default; 1815