1ac7ddfbfSEd Maste //===-- CommandObjectMemory.cpp ---------------------------------*- C++ -*-===//
2ac7ddfbfSEd Maste //
3ac7ddfbfSEd Maste // The LLVM Compiler Infrastructure
4ac7ddfbfSEd Maste //
5ac7ddfbfSEd Maste // This file is distributed under the University of Illinois Open Source
6ac7ddfbfSEd Maste // License. See LICENSE.TXT for details.
7ac7ddfbfSEd Maste //
8ac7ddfbfSEd Maste //===----------------------------------------------------------------------===//
9ac7ddfbfSEd Maste
1035617911SEd Maste #include <inttypes.h>
1135617911SEd Maste
121c3bbb01SEd Maste #include "clang/AST/Decl.h"
134bb0738eSEd Maste
144bb0738eSEd Maste #include "CommandObjectMemory.h"
154bb0738eSEd Maste #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h"
16ac7ddfbfSEd Maste #include "lldb/Core/Debugger.h"
17f678e45dSDimitry Andric #include "lldb/Core/DumpDataExtractor.h"
18ac7ddfbfSEd Maste #include "lldb/Core/Module.h"
194bb0738eSEd Maste #include "lldb/Core/Section.h"
20ac7ddfbfSEd Maste #include "lldb/Core/ValueObjectMemory.h"
2135617911SEd Maste #include "lldb/DataFormatters/ValueObjectPrinter.h"
22f678e45dSDimitry Andric #include "lldb/Host/OptionParser.h"
23ac7ddfbfSEd Maste #include "lldb/Interpreter/CommandInterpreter.h"
244bb0738eSEd Maste #include "lldb/Interpreter/CommandReturnObject.h"
254ba319b5SDimitry Andric #include "lldb/Interpreter/OptionArgParser.h"
26ac7ddfbfSEd Maste #include "lldb/Interpreter/OptionGroupFormat.h"
27ac7ddfbfSEd Maste #include "lldb/Interpreter/OptionGroupOutputFile.h"
28ac7ddfbfSEd Maste #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
29ac7ddfbfSEd Maste #include "lldb/Interpreter/OptionValueString.h"
304bb0738eSEd Maste #include "lldb/Interpreter/Options.h"
319f2f44ceSEd Maste #include "lldb/Symbol/ClangASTContext.h"
324bb0738eSEd Maste #include "lldb/Symbol/SymbolFile.h"
33ac7ddfbfSEd Maste #include "lldb/Symbol/TypeList.h"
347aa51b79SEd Maste #include "lldb/Target/MemoryHistory.h"
354bb0738eSEd Maste #include "lldb/Target/MemoryRegionInfo.h"
36ac7ddfbfSEd Maste #include "lldb/Target/Process.h"
37ac7ddfbfSEd Maste #include "lldb/Target/StackFrame.h"
387aa51b79SEd Maste #include "lldb/Target/Thread.h"
394ba319b5SDimitry Andric #include "lldb/Utility/Args.h"
40f678e45dSDimitry Andric #include "lldb/Utility/DataBufferHeap.h"
41f678e45dSDimitry Andric #include "lldb/Utility/DataBufferLLVM.h"
42f678e45dSDimitry Andric #include "lldb/Utility/StreamString.h"
43ac7ddfbfSEd Maste
444bb0738eSEd Maste #include "lldb/lldb-private.h"
454bb0738eSEd Maste
46ac7ddfbfSEd Maste using namespace lldb;
47ac7ddfbfSEd Maste using namespace lldb_private;
48ac7ddfbfSEd Maste
49*b5893f02SDimitry Andric static constexpr OptionDefinition g_read_memory_options[] = {
50435933ddSDimitry Andric // clang-format off
51*b5893f02SDimitry Andric {LLDB_OPT_SET_1, false, "num-per-line", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNumberPerLine, "The number of items per line to display." },
52*b5893f02SDimitry Andric {LLDB_OPT_SET_2, false, "binary", 'b', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "If true, memory will be saved as binary. If false, the memory is saved save as an ASCII dump that "
53435933ddSDimitry Andric "uses the format, size, count and number per line settings." },
54*b5893f02SDimitry Andric {LLDB_OPT_SET_3, true , "type", 't', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNone, "The name of a type to view memory as." },
55*b5893f02SDimitry Andric {LLDB_OPT_SET_3, false, "offset", 'E', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "How many elements of the specified type to skip before starting to display data." },
56ac7ddfbfSEd Maste {LLDB_OPT_SET_1 |
57ac7ddfbfSEd Maste LLDB_OPT_SET_2 |
58*b5893f02SDimitry Andric LLDB_OPT_SET_3, false, "force", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Necessary if reading over target.max-memory-read-size bytes." },
59435933ddSDimitry Andric // clang-format on
60ac7ddfbfSEd Maste };
61ac7ddfbfSEd Maste
62435933ddSDimitry Andric class OptionGroupReadMemory : public OptionGroup {
63ac7ddfbfSEd Maste public:
OptionGroupReadMemory()64435933ddSDimitry Andric OptionGroupReadMemory()
65435933ddSDimitry Andric : m_num_per_line(1, 1), m_output_as_binary(false), m_view_as_type(),
66435933ddSDimitry Andric m_offset(0, 0) {}
67ac7ddfbfSEd Maste
684bb0738eSEd Maste ~OptionGroupReadMemory() override = default;
69ac7ddfbfSEd Maste
GetDefinitions()70435933ddSDimitry Andric llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
71435933ddSDimitry Andric return llvm::makeArrayRef(g_read_memory_options);
72ac7ddfbfSEd Maste }
73ac7ddfbfSEd Maste
SetOptionValue(uint32_t option_idx,llvm::StringRef option_value,ExecutionContext * execution_context)745517e702SDimitry Andric Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
75435933ddSDimitry Andric ExecutionContext *execution_context) override {
765517e702SDimitry Andric Status error;
77435933ddSDimitry Andric const int short_option = g_read_memory_options[option_idx].short_option;
78ac7ddfbfSEd Maste
79435933ddSDimitry Andric switch (short_option) {
80ac7ddfbfSEd Maste case 'l':
81435933ddSDimitry Andric error = m_num_per_line.SetValueFromString(option_value);
82ac7ddfbfSEd Maste if (m_num_per_line.GetCurrentValue() == 0)
83435933ddSDimitry Andric error.SetErrorStringWithFormat(
84435933ddSDimitry Andric "invalid value for --num-per-line option '%s'",
85435933ddSDimitry Andric option_value.str().c_str());
86ac7ddfbfSEd Maste break;
87ac7ddfbfSEd Maste
88ac7ddfbfSEd Maste case 'b':
89ac7ddfbfSEd Maste m_output_as_binary = true;
90ac7ddfbfSEd Maste break;
91ac7ddfbfSEd Maste
92ac7ddfbfSEd Maste case 't':
93435933ddSDimitry Andric error = m_view_as_type.SetValueFromString(option_value);
94ac7ddfbfSEd Maste break;
95ac7ddfbfSEd Maste
96ac7ddfbfSEd Maste case 'r':
97ac7ddfbfSEd Maste m_force = true;
98ac7ddfbfSEd Maste break;
99ac7ddfbfSEd Maste
1009f2f44ceSEd Maste case 'E':
101435933ddSDimitry Andric error = m_offset.SetValueFromString(option_value);
1029f2f44ceSEd Maste break;
1039f2f44ceSEd Maste
104ac7ddfbfSEd Maste default:
105435933ddSDimitry Andric error.SetErrorStringWithFormat("unrecognized short option '%c'",
106435933ddSDimitry Andric short_option);
107ac7ddfbfSEd Maste break;
108ac7ddfbfSEd Maste }
109ac7ddfbfSEd Maste return error;
110ac7ddfbfSEd Maste }
111ac7ddfbfSEd Maste
OptionParsingStarting(ExecutionContext * execution_context)112435933ddSDimitry Andric void OptionParsingStarting(ExecutionContext *execution_context) override {
113ac7ddfbfSEd Maste m_num_per_line.Clear();
114ac7ddfbfSEd Maste m_output_as_binary = false;
115ac7ddfbfSEd Maste m_view_as_type.Clear();
116ac7ddfbfSEd Maste m_force = false;
1179f2f44ceSEd Maste m_offset.Clear();
118ac7ddfbfSEd Maste }
119ac7ddfbfSEd Maste
FinalizeSettings(Target * target,OptionGroupFormat & format_options)1205517e702SDimitry Andric Status FinalizeSettings(Target *target, OptionGroupFormat &format_options) {
1215517e702SDimitry Andric Status error;
122ac7ddfbfSEd Maste OptionValueUInt64 &byte_size_value = format_options.GetByteSizeValue();
123ac7ddfbfSEd Maste OptionValueUInt64 &count_value = format_options.GetCountValue();
124ac7ddfbfSEd Maste const bool byte_size_option_set = byte_size_value.OptionWasSet();
125ac7ddfbfSEd Maste const bool num_per_line_option_set = m_num_per_line.OptionWasSet();
126ac7ddfbfSEd Maste const bool count_option_set = format_options.GetCountValue().OptionWasSet();
127ac7ddfbfSEd Maste
128435933ddSDimitry Andric switch (format_options.GetFormat()) {
129ac7ddfbfSEd Maste default:
130ac7ddfbfSEd Maste break;
131ac7ddfbfSEd Maste
132ac7ddfbfSEd Maste case eFormatBoolean:
133ac7ddfbfSEd Maste if (!byte_size_option_set)
134ac7ddfbfSEd Maste byte_size_value = 1;
135ac7ddfbfSEd Maste if (!num_per_line_option_set)
136ac7ddfbfSEd Maste m_num_per_line = 1;
137ac7ddfbfSEd Maste if (!count_option_set)
138ac7ddfbfSEd Maste format_options.GetCountValue() = 8;
139ac7ddfbfSEd Maste break;
140ac7ddfbfSEd Maste
141ac7ddfbfSEd Maste case eFormatCString:
142ac7ddfbfSEd Maste break;
143ac7ddfbfSEd Maste
144ac7ddfbfSEd Maste case eFormatInstruction:
145ac7ddfbfSEd Maste if (count_option_set)
146ac7ddfbfSEd Maste byte_size_value = target->GetArchitecture().GetMaximumOpcodeByteSize();
147ac7ddfbfSEd Maste m_num_per_line = 1;
148ac7ddfbfSEd Maste break;
149ac7ddfbfSEd Maste
150ac7ddfbfSEd Maste case eFormatAddressInfo:
151ac7ddfbfSEd Maste if (!byte_size_option_set)
152ac7ddfbfSEd Maste byte_size_value = target->GetArchitecture().GetAddressByteSize();
153ac7ddfbfSEd Maste m_num_per_line = 1;
154ac7ddfbfSEd Maste if (!count_option_set)
155ac7ddfbfSEd Maste format_options.GetCountValue() = 8;
156ac7ddfbfSEd Maste break;
157ac7ddfbfSEd Maste
158ac7ddfbfSEd Maste case eFormatPointer:
159ac7ddfbfSEd Maste byte_size_value = target->GetArchitecture().GetAddressByteSize();
160ac7ddfbfSEd Maste if (!num_per_line_option_set)
161ac7ddfbfSEd Maste m_num_per_line = 4;
162ac7ddfbfSEd Maste if (!count_option_set)
163ac7ddfbfSEd Maste format_options.GetCountValue() = 8;
164ac7ddfbfSEd Maste break;
165ac7ddfbfSEd Maste
166ac7ddfbfSEd Maste case eFormatBinary:
167ac7ddfbfSEd Maste case eFormatFloat:
168ac7ddfbfSEd Maste case eFormatOctal:
169ac7ddfbfSEd Maste case eFormatDecimal:
170ac7ddfbfSEd Maste case eFormatEnum:
171ac7ddfbfSEd Maste case eFormatUnicode16:
172ac7ddfbfSEd Maste case eFormatUnicode32:
173ac7ddfbfSEd Maste case eFormatUnsigned:
174ac7ddfbfSEd Maste case eFormatHexFloat:
175ac7ddfbfSEd Maste if (!byte_size_option_set)
176ac7ddfbfSEd Maste byte_size_value = 4;
177ac7ddfbfSEd Maste if (!num_per_line_option_set)
178ac7ddfbfSEd Maste m_num_per_line = 1;
179ac7ddfbfSEd Maste if (!count_option_set)
180ac7ddfbfSEd Maste format_options.GetCountValue() = 8;
181ac7ddfbfSEd Maste break;
182ac7ddfbfSEd Maste
183ac7ddfbfSEd Maste case eFormatBytes:
184ac7ddfbfSEd Maste case eFormatBytesWithASCII:
185435933ddSDimitry Andric if (byte_size_option_set) {
186ac7ddfbfSEd Maste if (byte_size_value > 1)
1874bb0738eSEd Maste error.SetErrorStringWithFormat(
188435933ddSDimitry Andric "display format (bytes/bytes with ASCII) conflicts with the "
189435933ddSDimitry Andric "specified byte size %" PRIu64 "\n"
190435933ddSDimitry Andric "\tconsider using a different display format or don't specify "
191435933ddSDimitry Andric "the byte size.",
192ac7ddfbfSEd Maste byte_size_value.GetCurrentValue());
193435933ddSDimitry Andric } else
194ac7ddfbfSEd Maste byte_size_value = 1;
195ac7ddfbfSEd Maste if (!num_per_line_option_set)
196ac7ddfbfSEd Maste m_num_per_line = 16;
197ac7ddfbfSEd Maste if (!count_option_set)
198ac7ddfbfSEd Maste format_options.GetCountValue() = 32;
199ac7ddfbfSEd Maste break;
2004bb0738eSEd Maste
201ac7ddfbfSEd Maste case eFormatCharArray:
202ac7ddfbfSEd Maste case eFormatChar:
203ac7ddfbfSEd Maste case eFormatCharPrintable:
204ac7ddfbfSEd Maste if (!byte_size_option_set)
205ac7ddfbfSEd Maste byte_size_value = 1;
206ac7ddfbfSEd Maste if (!num_per_line_option_set)
207ac7ddfbfSEd Maste m_num_per_line = 32;
208ac7ddfbfSEd Maste if (!count_option_set)
209ac7ddfbfSEd Maste format_options.GetCountValue() = 64;
210ac7ddfbfSEd Maste break;
2114bb0738eSEd Maste
212ac7ddfbfSEd Maste case eFormatComplex:
213ac7ddfbfSEd Maste if (!byte_size_option_set)
214ac7ddfbfSEd Maste byte_size_value = 8;
215ac7ddfbfSEd Maste if (!num_per_line_option_set)
216ac7ddfbfSEd Maste m_num_per_line = 1;
217ac7ddfbfSEd Maste if (!count_option_set)
218ac7ddfbfSEd Maste format_options.GetCountValue() = 8;
219ac7ddfbfSEd Maste break;
2204bb0738eSEd Maste
221ac7ddfbfSEd Maste case eFormatComplexInteger:
222ac7ddfbfSEd Maste if (!byte_size_option_set)
223ac7ddfbfSEd Maste byte_size_value = 8;
224ac7ddfbfSEd Maste if (!num_per_line_option_set)
225ac7ddfbfSEd Maste m_num_per_line = 1;
226ac7ddfbfSEd Maste if (!count_option_set)
227ac7ddfbfSEd Maste format_options.GetCountValue() = 8;
228ac7ddfbfSEd Maste break;
2294bb0738eSEd Maste
230ac7ddfbfSEd Maste case eFormatHex:
231ac7ddfbfSEd Maste if (!byte_size_option_set)
232ac7ddfbfSEd Maste byte_size_value = 4;
233435933ddSDimitry Andric if (!num_per_line_option_set) {
234435933ddSDimitry Andric switch (byte_size_value) {
235ac7ddfbfSEd Maste case 1:
236ac7ddfbfSEd Maste case 2:
237ac7ddfbfSEd Maste m_num_per_line = 8;
238ac7ddfbfSEd Maste break;
239ac7ddfbfSEd Maste case 4:
240ac7ddfbfSEd Maste m_num_per_line = 4;
241ac7ddfbfSEd Maste break;
242ac7ddfbfSEd Maste case 8:
243ac7ddfbfSEd Maste m_num_per_line = 2;
244ac7ddfbfSEd Maste break;
245ac7ddfbfSEd Maste default:
246ac7ddfbfSEd Maste m_num_per_line = 1;
247ac7ddfbfSEd Maste break;
248ac7ddfbfSEd Maste }
249ac7ddfbfSEd Maste }
250ac7ddfbfSEd Maste if (!count_option_set)
251ac7ddfbfSEd Maste count_value = 8;
252ac7ddfbfSEd Maste break;
253ac7ddfbfSEd Maste
254ac7ddfbfSEd Maste case eFormatVectorOfChar:
255ac7ddfbfSEd Maste case eFormatVectorOfSInt8:
256ac7ddfbfSEd Maste case eFormatVectorOfUInt8:
257ac7ddfbfSEd Maste case eFormatVectorOfSInt16:
258ac7ddfbfSEd Maste case eFormatVectorOfUInt16:
259ac7ddfbfSEd Maste case eFormatVectorOfSInt32:
260ac7ddfbfSEd Maste case eFormatVectorOfUInt32:
261ac7ddfbfSEd Maste case eFormatVectorOfSInt64:
262ac7ddfbfSEd Maste case eFormatVectorOfUInt64:
2639f2f44ceSEd Maste case eFormatVectorOfFloat16:
264ac7ddfbfSEd Maste case eFormatVectorOfFloat32:
265ac7ddfbfSEd Maste case eFormatVectorOfFloat64:
266ac7ddfbfSEd Maste case eFormatVectorOfUInt128:
267ac7ddfbfSEd Maste if (!byte_size_option_set)
268ac7ddfbfSEd Maste byte_size_value = 128;
269ac7ddfbfSEd Maste if (!num_per_line_option_set)
270ac7ddfbfSEd Maste m_num_per_line = 1;
271ac7ddfbfSEd Maste if (!count_option_set)
272ac7ddfbfSEd Maste count_value = 4;
273ac7ddfbfSEd Maste break;
274ac7ddfbfSEd Maste }
275ac7ddfbfSEd Maste return error;
276ac7ddfbfSEd Maste }
277ac7ddfbfSEd Maste
AnyOptionWasSet() const278435933ddSDimitry Andric bool AnyOptionWasSet() const {
279435933ddSDimitry Andric return m_num_per_line.OptionWasSet() || m_output_as_binary ||
280435933ddSDimitry Andric m_view_as_type.OptionWasSet() || m_offset.OptionWasSet();
281ac7ddfbfSEd Maste }
282ac7ddfbfSEd Maste
283ac7ddfbfSEd Maste OptionValueUInt64 m_num_per_line;
284ac7ddfbfSEd Maste bool m_output_as_binary;
285ac7ddfbfSEd Maste OptionValueString m_view_as_type;
286ac7ddfbfSEd Maste bool m_force;
2879f2f44ceSEd Maste OptionValueUInt64 m_offset;
288ac7ddfbfSEd Maste };
289ac7ddfbfSEd Maste
290ac7ddfbfSEd Maste //----------------------------------------------------------------------
291ac7ddfbfSEd Maste // Read memory from the inferior process
292ac7ddfbfSEd Maste //----------------------------------------------------------------------
293435933ddSDimitry Andric class CommandObjectMemoryRead : public CommandObjectParsed {
294ac7ddfbfSEd Maste public:
CommandObjectMemoryRead(CommandInterpreter & interpreter)2954bb0738eSEd Maste CommandObjectMemoryRead(CommandInterpreter &interpreter)
296435933ddSDimitry Andric : CommandObjectParsed(
297435933ddSDimitry Andric interpreter, "memory read",
298435933ddSDimitry Andric "Read from the memory of the current target process.", nullptr,
299435933ddSDimitry Andric eCommandRequiresTarget | eCommandProcessMustBePaused),
300435933ddSDimitry Andric m_option_group(), m_format_options(eFormatBytesWithASCII, 1, 8),
301435933ddSDimitry Andric m_memory_options(), m_outfile_options(), m_varobj_options(),
302435933ddSDimitry Andric m_next_addr(LLDB_INVALID_ADDRESS), m_prev_byte_size(0),
303ac7ddfbfSEd Maste m_prev_format_options(eFormatBytesWithASCII, 1, 8),
304435933ddSDimitry Andric m_prev_memory_options(), m_prev_outfile_options(),
305435933ddSDimitry Andric m_prev_varobj_options() {
306ac7ddfbfSEd Maste CommandArgumentEntry arg1;
307ac7ddfbfSEd Maste CommandArgumentEntry arg2;
308ac7ddfbfSEd Maste CommandArgumentData start_addr_arg;
309ac7ddfbfSEd Maste CommandArgumentData end_addr_arg;
310ac7ddfbfSEd Maste
311ac7ddfbfSEd Maste // Define the first (and only) variant of this arg.
312ac7ddfbfSEd Maste start_addr_arg.arg_type = eArgTypeAddressOrExpression;
313ac7ddfbfSEd Maste start_addr_arg.arg_repetition = eArgRepeatPlain;
314ac7ddfbfSEd Maste
315435933ddSDimitry Andric // There is only one variant this argument could be; put it into the
316435933ddSDimitry Andric // argument entry.
317ac7ddfbfSEd Maste arg1.push_back(start_addr_arg);
318ac7ddfbfSEd Maste
319ac7ddfbfSEd Maste // Define the first (and only) variant of this arg.
320ac7ddfbfSEd Maste end_addr_arg.arg_type = eArgTypeAddressOrExpression;
321ac7ddfbfSEd Maste end_addr_arg.arg_repetition = eArgRepeatOptional;
322ac7ddfbfSEd Maste
323435933ddSDimitry Andric // There is only one variant this argument could be; put it into the
324435933ddSDimitry Andric // argument entry.
325ac7ddfbfSEd Maste arg2.push_back(end_addr_arg);
326ac7ddfbfSEd Maste
327ac7ddfbfSEd Maste // Push the data for the first argument into the m_arguments vector.
328ac7ddfbfSEd Maste m_arguments.push_back(arg1);
329ac7ddfbfSEd Maste m_arguments.push_back(arg2);
330ac7ddfbfSEd Maste
331ac7ddfbfSEd Maste // Add the "--format" and "--count" options to group 1 and 3
332ac7ddfbfSEd Maste m_option_group.Append(&m_format_options,
333435933ddSDimitry Andric OptionGroupFormat::OPTION_GROUP_FORMAT |
334435933ddSDimitry Andric OptionGroupFormat::OPTION_GROUP_COUNT,
335ac7ddfbfSEd Maste LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
336ac7ddfbfSEd Maste m_option_group.Append(&m_format_options,
337ac7ddfbfSEd Maste OptionGroupFormat::OPTION_GROUP_GDB_FMT,
338ac7ddfbfSEd Maste LLDB_OPT_SET_1 | LLDB_OPT_SET_3);
339ac7ddfbfSEd Maste // Add the "--size" option to group 1 and 2
340ac7ddfbfSEd Maste m_option_group.Append(&m_format_options,
341ac7ddfbfSEd Maste OptionGroupFormat::OPTION_GROUP_SIZE,
342ac7ddfbfSEd Maste LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
343ac7ddfbfSEd Maste m_option_group.Append(&m_memory_options);
344435933ddSDimitry Andric m_option_group.Append(&m_outfile_options, LLDB_OPT_SET_ALL,
345435933ddSDimitry Andric LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3);
346ac7ddfbfSEd Maste m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_3);
347ac7ddfbfSEd Maste m_option_group.Finalize();
348ac7ddfbfSEd Maste }
349ac7ddfbfSEd Maste
3504bb0738eSEd Maste ~CommandObjectMemoryRead() override = default;
351ac7ddfbfSEd Maste
GetOptions()352435933ddSDimitry Andric Options *GetOptions() override { return &m_option_group; }
353ac7ddfbfSEd Maste
GetRepeatCommand(Args & current_command_args,uint32_t index)354435933ddSDimitry Andric const char *GetRepeatCommand(Args ¤t_command_args,
355435933ddSDimitry Andric uint32_t index) override {
356ac7ddfbfSEd Maste return m_cmd_name.c_str();
357ac7ddfbfSEd Maste }
358ac7ddfbfSEd Maste
359ac7ddfbfSEd Maste protected:
DoExecute(Args & command,CommandReturnObject & result)360435933ddSDimitry Andric bool DoExecute(Args &command, CommandReturnObject &result) override {
361435933ddSDimitry Andric // No need to check "target" for validity as eCommandRequiresTarget ensures
362435933ddSDimitry Andric // it is valid
363ac7ddfbfSEd Maste Target *target = m_exe_ctx.GetTargetPtr();
364ac7ddfbfSEd Maste
365ac7ddfbfSEd Maste const size_t argc = command.GetArgumentCount();
366ac7ddfbfSEd Maste
367435933ddSDimitry Andric if ((argc == 0 && m_next_addr == LLDB_INVALID_ADDRESS) || argc > 2) {
368435933ddSDimitry Andric result.AppendErrorWithFormat("%s takes a start address expression with "
369435933ddSDimitry Andric "an optional end address expression.\n",
370435933ddSDimitry Andric m_cmd_name.c_str());
371435933ddSDimitry Andric result.AppendRawWarning("Expressions should be quoted if they contain "
372435933ddSDimitry Andric "spaces or other special characters.\n");
373ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
374ac7ddfbfSEd Maste return false;
375ac7ddfbfSEd Maste }
376ac7ddfbfSEd Maste
3779f2f44ceSEd Maste CompilerType clang_ast_type;
3785517e702SDimitry Andric Status error;
379ac7ddfbfSEd Maste
380435933ddSDimitry Andric const char *view_as_type_cstr =
381435933ddSDimitry Andric m_memory_options.m_view_as_type.GetCurrentValue();
382435933ddSDimitry Andric if (view_as_type_cstr && view_as_type_cstr[0]) {
383ac7ddfbfSEd Maste // We are viewing memory as a type
384ac7ddfbfSEd Maste
385ac7ddfbfSEd Maste const bool exact_match = false;
386ac7ddfbfSEd Maste TypeList type_list;
387ac7ddfbfSEd Maste uint32_t reference_count = 0;
388ac7ddfbfSEd Maste uint32_t pointer_count = 0;
389ac7ddfbfSEd Maste size_t idx;
390ac7ddfbfSEd Maste
391ac7ddfbfSEd Maste #define ALL_KEYWORDS \
392ac7ddfbfSEd Maste KEYWORD("const") \
393ac7ddfbfSEd Maste KEYWORD("volatile") \
394ac7ddfbfSEd Maste KEYWORD("restrict") \
395ac7ddfbfSEd Maste KEYWORD("struct") \
396ac7ddfbfSEd Maste KEYWORD("class") \
397ac7ddfbfSEd Maste KEYWORD("union")
398ac7ddfbfSEd Maste
399ac7ddfbfSEd Maste #define KEYWORD(s) s,
400435933ddSDimitry Andric static const char *g_keywords[] = {ALL_KEYWORDS};
401ac7ddfbfSEd Maste #undef KEYWORD
402ac7ddfbfSEd Maste
403ac7ddfbfSEd Maste #define KEYWORD(s) (sizeof(s) - 1),
404435933ddSDimitry Andric static const int g_keyword_lengths[] = {ALL_KEYWORDS};
405ac7ddfbfSEd Maste #undef KEYWORD
406ac7ddfbfSEd Maste
407ac7ddfbfSEd Maste #undef ALL_KEYWORDS
408ac7ddfbfSEd Maste
409ac7ddfbfSEd Maste static size_t g_num_keywords = sizeof(g_keywords) / sizeof(const char *);
410ac7ddfbfSEd Maste std::string type_str(view_as_type_cstr);
411ac7ddfbfSEd Maste
412ac7ddfbfSEd Maste // Remove all instances of g_keywords that are followed by spaces
413435933ddSDimitry Andric for (size_t i = 0; i < g_num_keywords; ++i) {
414ac7ddfbfSEd Maste const char *keyword = g_keywords[i];
415ac7ddfbfSEd Maste int keyword_len = g_keyword_lengths[i];
416ac7ddfbfSEd Maste
417ac7ddfbfSEd Maste idx = 0;
418435933ddSDimitry Andric while ((idx = type_str.find(keyword, idx)) != std::string::npos) {
419435933ddSDimitry Andric if (type_str[idx + keyword_len] == ' ' ||
420435933ddSDimitry Andric type_str[idx + keyword_len] == '\t') {
421ac7ddfbfSEd Maste type_str.erase(idx, keyword_len + 1);
422ac7ddfbfSEd Maste idx = 0;
423435933ddSDimitry Andric } else {
424ac7ddfbfSEd Maste idx += keyword_len;
425ac7ddfbfSEd Maste }
426ac7ddfbfSEd Maste }
427ac7ddfbfSEd Maste }
428ac7ddfbfSEd Maste bool done = type_str.empty();
429ac7ddfbfSEd Maste //
430ac7ddfbfSEd Maste idx = type_str.find_first_not_of(" \t");
431ac7ddfbfSEd Maste if (idx > 0 && idx != std::string::npos)
432ac7ddfbfSEd Maste type_str.erase(0, idx);
433435933ddSDimitry Andric while (!done) {
434ac7ddfbfSEd Maste // Strip trailing spaces
435ac7ddfbfSEd Maste if (type_str.empty())
436ac7ddfbfSEd Maste done = true;
437435933ddSDimitry Andric else {
438435933ddSDimitry Andric switch (type_str[type_str.size() - 1]) {
439ac7ddfbfSEd Maste case '*':
440ac7ddfbfSEd Maste ++pointer_count;
4414bb0738eSEd Maste LLVM_FALLTHROUGH;
442ac7ddfbfSEd Maste case ' ':
443ac7ddfbfSEd Maste case '\t':
444ac7ddfbfSEd Maste type_str.erase(type_str.size() - 1);
445ac7ddfbfSEd Maste break;
446ac7ddfbfSEd Maste
447ac7ddfbfSEd Maste case '&':
448435933ddSDimitry Andric if (reference_count == 0) {
449ac7ddfbfSEd Maste reference_count = 1;
450ac7ddfbfSEd Maste type_str.erase(type_str.size() - 1);
451435933ddSDimitry Andric } else {
452435933ddSDimitry Andric result.AppendErrorWithFormat("invalid type string: '%s'\n",
453435933ddSDimitry Andric view_as_type_cstr);
454ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
455ac7ddfbfSEd Maste return false;
456ac7ddfbfSEd Maste }
457ac7ddfbfSEd Maste break;
458ac7ddfbfSEd Maste
459ac7ddfbfSEd Maste default:
460ac7ddfbfSEd Maste done = true;
461ac7ddfbfSEd Maste break;
462ac7ddfbfSEd Maste }
463ac7ddfbfSEd Maste }
464ac7ddfbfSEd Maste }
465ac7ddfbfSEd Maste
4664bb0738eSEd Maste llvm::DenseSet<lldb_private::SymbolFile *> searched_symbol_files;
467ac7ddfbfSEd Maste ConstString lookup_type_name(type_str.c_str());
468ac7ddfbfSEd Maste StackFrame *frame = m_exe_ctx.GetFramePtr();
469*b5893f02SDimitry Andric ModuleSP search_first;
470435933ddSDimitry Andric if (frame) {
471*b5893f02SDimitry Andric search_first = frame->GetSymbolContext(eSymbolContextModule).module_sp;
472ac7ddfbfSEd Maste }
473*b5893f02SDimitry Andric target->GetImages().FindTypes(search_first.get(), lookup_type_name,
474*b5893f02SDimitry Andric exact_match, 1, searched_symbol_files,
475*b5893f02SDimitry Andric type_list);
476ac7ddfbfSEd Maste
477435933ddSDimitry Andric if (type_list.GetSize() == 0 && lookup_type_name.GetCString() &&
478435933ddSDimitry Andric *lookup_type_name.GetCString() == '$') {
479435933ddSDimitry Andric if (ClangPersistentVariables *persistent_vars =
480435933ddSDimitry Andric llvm::dyn_cast_or_null<ClangPersistentVariables>(
481435933ddSDimitry Andric target->GetPersistentExpressionStateForLanguage(
482435933ddSDimitry Andric lldb::eLanguageTypeC))) {
483435933ddSDimitry Andric clang::TypeDecl *tdecl = llvm::dyn_cast_or_null<clang::TypeDecl>(
484435933ddSDimitry Andric persistent_vars->GetPersistentDecl(
485435933ddSDimitry Andric ConstString(lookup_type_name)));
4869f2f44ceSEd Maste
487435933ddSDimitry Andric if (tdecl) {
488435933ddSDimitry Andric clang_ast_type.SetCompilerType(
489435933ddSDimitry Andric ClangASTContext::GetASTContext(&tdecl->getASTContext()),
490435933ddSDimitry Andric reinterpret_cast<lldb::opaque_compiler_type_t>(
491435933ddSDimitry Andric const_cast<clang::Type *>(tdecl->getTypeForDecl())));
4929f2f44ceSEd Maste }
493ac7ddfbfSEd Maste }
494ac7ddfbfSEd Maste }
495ac7ddfbfSEd Maste
496435933ddSDimitry Andric if (!clang_ast_type.IsValid()) {
497435933ddSDimitry Andric if (type_list.GetSize() == 0) {
498435933ddSDimitry Andric result.AppendErrorWithFormat("unable to find any types that match "
499435933ddSDimitry Andric "the raw type '%s' for full type '%s'\n",
500ac7ddfbfSEd Maste lookup_type_name.GetCString(),
501ac7ddfbfSEd Maste view_as_type_cstr);
502ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
503ac7ddfbfSEd Maste return false;
504435933ddSDimitry Andric } else {
505ac7ddfbfSEd Maste TypeSP type_sp(type_list.GetTypeAtIndex(0));
5069f2f44ceSEd Maste clang_ast_type = type_sp->GetFullCompilerType();
507ac7ddfbfSEd Maste }
508ac7ddfbfSEd Maste }
509ac7ddfbfSEd Maste
510435933ddSDimitry Andric while (pointer_count > 0) {
5119f2f44ceSEd Maste CompilerType pointer_type = clang_ast_type.GetPointerType();
512ac7ddfbfSEd Maste if (pointer_type.IsValid())
513ac7ddfbfSEd Maste clang_ast_type = pointer_type;
514435933ddSDimitry Andric else {
515ac7ddfbfSEd Maste result.AppendError("unable make a pointer type\n");
516ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
517ac7ddfbfSEd Maste return false;
518ac7ddfbfSEd Maste }
519ac7ddfbfSEd Maste --pointer_count;
520ac7ddfbfSEd Maste }
521ac7ddfbfSEd Maste
522*b5893f02SDimitry Andric llvm::Optional<uint64_t> size = clang_ast_type.GetByteSize(nullptr);
523*b5893f02SDimitry Andric if (!size) {
524435933ddSDimitry Andric result.AppendErrorWithFormat(
525435933ddSDimitry Andric "unable to get the byte size of the type '%s'\n",
526ac7ddfbfSEd Maste view_as_type_cstr);
527ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
528ac7ddfbfSEd Maste return false;
529ac7ddfbfSEd Maste }
530*b5893f02SDimitry Andric m_format_options.GetByteSizeValue() = *size;
531ac7ddfbfSEd Maste
532ac7ddfbfSEd Maste if (!m_format_options.GetCountValue().OptionWasSet())
533ac7ddfbfSEd Maste m_format_options.GetCountValue() = 1;
534435933ddSDimitry Andric } else {
535ac7ddfbfSEd Maste error = m_memory_options.FinalizeSettings(target, m_format_options);
536ac7ddfbfSEd Maste }
537ac7ddfbfSEd Maste
538ac7ddfbfSEd Maste // Look for invalid combinations of settings
539435933ddSDimitry Andric if (error.Fail()) {
540ac7ddfbfSEd Maste result.AppendError(error.AsCString());
541ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
542ac7ddfbfSEd Maste return false;
543ac7ddfbfSEd Maste }
544ac7ddfbfSEd Maste
545ac7ddfbfSEd Maste lldb::addr_t addr;
546ac7ddfbfSEd Maste size_t total_byte_size = 0;
547435933ddSDimitry Andric if (argc == 0) {
5484ba319b5SDimitry Andric // Use the last address and byte size and all options as they were if no
5494ba319b5SDimitry Andric // options have been set
550ac7ddfbfSEd Maste addr = m_next_addr;
551ac7ddfbfSEd Maste total_byte_size = m_prev_byte_size;
552ac7ddfbfSEd Maste clang_ast_type = m_prev_clang_ast_type;
553ac7ddfbfSEd Maste if (!m_format_options.AnyOptionWasSet() &&
554ac7ddfbfSEd Maste !m_memory_options.AnyOptionWasSet() &&
555ac7ddfbfSEd Maste !m_outfile_options.AnyOptionWasSet() &&
556435933ddSDimitry Andric !m_varobj_options.AnyOptionWasSet()) {
557ac7ddfbfSEd Maste m_format_options = m_prev_format_options;
558ac7ddfbfSEd Maste m_memory_options = m_prev_memory_options;
559ac7ddfbfSEd Maste m_outfile_options = m_prev_outfile_options;
560ac7ddfbfSEd Maste m_varobj_options = m_prev_varobj_options;
561ac7ddfbfSEd Maste }
562ac7ddfbfSEd Maste }
563ac7ddfbfSEd Maste
564ac7ddfbfSEd Maste size_t item_count = m_format_options.GetCountValue().GetCurrentValue();
5657aa51b79SEd Maste
5667aa51b79SEd Maste // TODO For non-8-bit byte addressable architectures this needs to be
5677aa51b79SEd Maste // revisited to fully support all lldb's range of formatting options.
5684ba319b5SDimitry Andric // Furthermore code memory reads (for those architectures) will not be
5694ba319b5SDimitry Andric // correctly formatted even w/o formatting options.
5707aa51b79SEd Maste size_t item_byte_size =
571435933ddSDimitry Andric target->GetArchitecture().GetDataByteSize() > 1
572435933ddSDimitry Andric ? target->GetArchitecture().GetDataByteSize()
573435933ddSDimitry Andric : m_format_options.GetByteSizeValue().GetCurrentValue();
5747aa51b79SEd Maste
575435933ddSDimitry Andric const size_t num_per_line =
576435933ddSDimitry Andric m_memory_options.m_num_per_line.GetCurrentValue();
577ac7ddfbfSEd Maste
578435933ddSDimitry Andric if (total_byte_size == 0) {
579ac7ddfbfSEd Maste total_byte_size = item_count * item_byte_size;
580ac7ddfbfSEd Maste if (total_byte_size == 0)
581ac7ddfbfSEd Maste total_byte_size = 32;
582ac7ddfbfSEd Maste }
583ac7ddfbfSEd Maste
584ac7ddfbfSEd Maste if (argc > 0)
5854ba319b5SDimitry Andric addr = OptionArgParser::ToAddress(&m_exe_ctx, command[0].ref,
586435933ddSDimitry Andric LLDB_INVALID_ADDRESS, &error);
587ac7ddfbfSEd Maste
588435933ddSDimitry Andric if (addr == LLDB_INVALID_ADDRESS) {
589ac7ddfbfSEd Maste result.AppendError("invalid start address expression.");
590ac7ddfbfSEd Maste result.AppendError(error.AsCString());
591ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
592ac7ddfbfSEd Maste return false;
593ac7ddfbfSEd Maste }
594ac7ddfbfSEd Maste
595435933ddSDimitry Andric if (argc == 2) {
5964ba319b5SDimitry Andric lldb::addr_t end_addr = OptionArgParser::ToAddress(
597435933ddSDimitry Andric &m_exe_ctx, command[1].ref, LLDB_INVALID_ADDRESS, nullptr);
598435933ddSDimitry Andric if (end_addr == LLDB_INVALID_ADDRESS) {
599ac7ddfbfSEd Maste result.AppendError("invalid end address expression.");
600ac7ddfbfSEd Maste result.AppendError(error.AsCString());
601ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
602ac7ddfbfSEd Maste return false;
603435933ddSDimitry Andric } else if (end_addr <= addr) {
604435933ddSDimitry Andric result.AppendErrorWithFormat(
605435933ddSDimitry Andric "end address (0x%" PRIx64
606435933ddSDimitry Andric ") must be greater that the start address (0x%" PRIx64 ").\n",
607435933ddSDimitry Andric end_addr, addr);
608ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
609ac7ddfbfSEd Maste return false;
610435933ddSDimitry Andric } else if (m_format_options.GetCountValue().OptionWasSet()) {
611435933ddSDimitry Andric result.AppendErrorWithFormat(
612435933ddSDimitry Andric "specify either the end address (0x%" PRIx64
613435933ddSDimitry Andric ") or the count (--count %" PRIu64 "), not both.\n",
614435933ddSDimitry Andric end_addr, (uint64_t)item_count);
615ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
616ac7ddfbfSEd Maste return false;
617ac7ddfbfSEd Maste }
618ac7ddfbfSEd Maste
619ac7ddfbfSEd Maste total_byte_size = end_addr - addr;
620ac7ddfbfSEd Maste item_count = total_byte_size / item_byte_size;
621ac7ddfbfSEd Maste }
622ac7ddfbfSEd Maste
623ac7ddfbfSEd Maste uint32_t max_unforced_size = target->GetMaximumMemReadSize();
624ac7ddfbfSEd Maste
625435933ddSDimitry Andric if (total_byte_size > max_unforced_size && !m_memory_options.m_force) {
626435933ddSDimitry Andric result.AppendErrorWithFormat(
627435933ddSDimitry Andric "Normally, \'memory read\' will not read over %" PRIu32
628435933ddSDimitry Andric " bytes of data.\n",
629435933ddSDimitry Andric max_unforced_size);
630435933ddSDimitry Andric result.AppendErrorWithFormat(
631435933ddSDimitry Andric "Please use --force to override this restriction just once.\n");
632435933ddSDimitry Andric result.AppendErrorWithFormat("or set target.max-memory-read-size if you "
633435933ddSDimitry Andric "will often need a larger limit.\n");
634ac7ddfbfSEd Maste return false;
635ac7ddfbfSEd Maste }
636ac7ddfbfSEd Maste
637ac7ddfbfSEd Maste DataBufferSP data_sp;
638ac7ddfbfSEd Maste size_t bytes_read = 0;
639435933ddSDimitry Andric if (clang_ast_type.GetOpaqueQualType()) {
640435933ddSDimitry Andric // Make sure we don't display our type as ASCII bytes like the default
641435933ddSDimitry Andric // memory read
6424bb0738eSEd Maste if (!m_format_options.GetFormatValue().OptionWasSet())
643ac7ddfbfSEd Maste m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault);
644ac7ddfbfSEd Maste
645*b5893f02SDimitry Andric llvm::Optional<uint64_t> size = clang_ast_type.GetByteSize(nullptr);
646*b5893f02SDimitry Andric if (!size) {
647*b5893f02SDimitry Andric result.AppendError("can't get size of type");
648*b5893f02SDimitry Andric return false;
649*b5893f02SDimitry Andric }
650*b5893f02SDimitry Andric bytes_read = *size * m_format_options.GetCountValue().GetCurrentValue();
6519f2f44ceSEd Maste
6529f2f44ceSEd Maste if (argc > 0)
653*b5893f02SDimitry Andric addr = addr + (*size * m_memory_options.m_offset.GetCurrentValue());
654435933ddSDimitry Andric } else if (m_format_options.GetFormatValue().GetCurrentValue() !=
655435933ddSDimitry Andric eFormatCString) {
656ac7ddfbfSEd Maste data_sp.reset(new DataBufferHeap(total_byte_size, '\0'));
657435933ddSDimitry Andric if (data_sp->GetBytes() == nullptr) {
658435933ddSDimitry Andric result.AppendErrorWithFormat(
659435933ddSDimitry Andric "can't allocate 0x%" PRIx32
660435933ddSDimitry Andric " bytes for the memory read buffer, specify a smaller size to read",
661435933ddSDimitry Andric (uint32_t)total_byte_size);
662ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
663ac7ddfbfSEd Maste return false;
664ac7ddfbfSEd Maste }
665ac7ddfbfSEd Maste
6664bb0738eSEd Maste Address address(addr, nullptr);
667435933ddSDimitry Andric bytes_read = target->ReadMemory(address, false, data_sp->GetBytes(),
668435933ddSDimitry Andric data_sp->GetByteSize(), error);
669435933ddSDimitry Andric if (bytes_read == 0) {
670ac7ddfbfSEd Maste const char *error_cstr = error.AsCString();
671435933ddSDimitry Andric if (error_cstr && error_cstr[0]) {
672ac7ddfbfSEd Maste result.AppendError(error_cstr);
673435933ddSDimitry Andric } else {
674435933ddSDimitry Andric result.AppendErrorWithFormat(
675435933ddSDimitry Andric "failed to read memory from 0x%" PRIx64 ".\n", addr);
676ac7ddfbfSEd Maste }
677ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
678ac7ddfbfSEd Maste return false;
679ac7ddfbfSEd Maste }
680ac7ddfbfSEd Maste
681ac7ddfbfSEd Maste if (bytes_read < total_byte_size)
682435933ddSDimitry Andric result.AppendWarningWithFormat(
683435933ddSDimitry Andric "Not all bytes (%" PRIu64 "/%" PRIu64
684435933ddSDimitry Andric ") were able to be read from 0x%" PRIx64 ".\n",
685435933ddSDimitry Andric (uint64_t)bytes_read, (uint64_t)total_byte_size, addr);
686435933ddSDimitry Andric } else {
687435933ddSDimitry Andric // we treat c-strings as a special case because they do not have a fixed
688435933ddSDimitry Andric // size
689435933ddSDimitry Andric if (m_format_options.GetByteSizeValue().OptionWasSet() &&
690435933ddSDimitry Andric !m_format_options.HasGDBFormat())
691ac7ddfbfSEd Maste item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue();
692ac7ddfbfSEd Maste else
693ac7ddfbfSEd Maste item_byte_size = target->GetMaximumSizeOfStringSummary();
694ac7ddfbfSEd Maste if (!m_format_options.GetCountValue().OptionWasSet())
695ac7ddfbfSEd Maste item_count = 1;
696435933ddSDimitry Andric data_sp.reset(new DataBufferHeap((item_byte_size + 1) * item_count,
697435933ddSDimitry Andric '\0')); // account for NULLs as necessary
698435933ddSDimitry Andric if (data_sp->GetBytes() == nullptr) {
699435933ddSDimitry Andric result.AppendErrorWithFormat(
700435933ddSDimitry Andric "can't allocate 0x%" PRIx64
701435933ddSDimitry Andric " bytes for the memory read buffer, specify a smaller size to read",
702435933ddSDimitry Andric (uint64_t)((item_byte_size + 1) * item_count));
703ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
704ac7ddfbfSEd Maste return false;
705ac7ddfbfSEd Maste }
706ac7ddfbfSEd Maste uint8_t *data_ptr = data_sp->GetBytes();
707ac7ddfbfSEd Maste auto data_addr = addr;
708ac7ddfbfSEd Maste auto count = item_count;
709ac7ddfbfSEd Maste item_count = 0;
7101c3bbb01SEd Maste bool break_on_no_NULL = false;
711435933ddSDimitry Andric while (item_count < count) {
712ac7ddfbfSEd Maste std::string buffer;
713ac7ddfbfSEd Maste buffer.resize(item_byte_size + 1, 0);
7145517e702SDimitry Andric Status error;
715435933ddSDimitry Andric size_t read = target->ReadCStringFromMemory(data_addr, &buffer[0],
716435933ddSDimitry Andric item_byte_size + 1, error);
717435933ddSDimitry Andric if (error.Fail()) {
718435933ddSDimitry Andric result.AppendErrorWithFormat(
719435933ddSDimitry Andric "failed to read memory from 0x%" PRIx64 ".\n", addr);
720ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
721ac7ddfbfSEd Maste return false;
722ac7ddfbfSEd Maste }
7231c3bbb01SEd Maste
724435933ddSDimitry Andric if (item_byte_size == read) {
725435933ddSDimitry Andric result.AppendWarningWithFormat(
726435933ddSDimitry Andric "unable to find a NULL terminated string at 0x%" PRIx64
727435933ddSDimitry Andric ".Consider increasing the maximum read length.\n",
728435933ddSDimitry Andric data_addr);
7291c3bbb01SEd Maste --read;
7301c3bbb01SEd Maste break_on_no_NULL = true;
731435933ddSDimitry Andric } else
7321c3bbb01SEd Maste ++read; // account for final NULL byte
7331c3bbb01SEd Maste
734ac7ddfbfSEd Maste memcpy(data_ptr, &buffer[0], read);
735ac7ddfbfSEd Maste data_ptr += read;
736ac7ddfbfSEd Maste data_addr += read;
737ac7ddfbfSEd Maste bytes_read += read;
738435933ddSDimitry Andric item_count++; // if we break early we know we only read item_count
739435933ddSDimitry Andric // strings
7401c3bbb01SEd Maste
7411c3bbb01SEd Maste if (break_on_no_NULL)
7421c3bbb01SEd Maste break;
743ac7ddfbfSEd Maste }
744ac7ddfbfSEd Maste data_sp.reset(new DataBufferHeap(data_sp->GetBytes(), bytes_read + 1));
745ac7ddfbfSEd Maste }
746ac7ddfbfSEd Maste
747ac7ddfbfSEd Maste m_next_addr = addr + bytes_read;
748ac7ddfbfSEd Maste m_prev_byte_size = bytes_read;
749ac7ddfbfSEd Maste m_prev_format_options = m_format_options;
750ac7ddfbfSEd Maste m_prev_memory_options = m_memory_options;
751ac7ddfbfSEd Maste m_prev_outfile_options = m_outfile_options;
752ac7ddfbfSEd Maste m_prev_varobj_options = m_varobj_options;
753ac7ddfbfSEd Maste m_prev_clang_ast_type = clang_ast_type;
754ac7ddfbfSEd Maste
755ac7ddfbfSEd Maste StreamFile outfile_stream;
7564bb0738eSEd Maste Stream *output_stream = nullptr;
757435933ddSDimitry Andric const FileSpec &outfile_spec =
758435933ddSDimitry Andric m_outfile_options.GetFile().GetCurrentValue();
759*b5893f02SDimitry Andric
760*b5893f02SDimitry Andric std::string path = outfile_spec.GetPath();
761435933ddSDimitry Andric if (outfile_spec) {
762ac7ddfbfSEd Maste
763435933ddSDimitry Andric uint32_t open_options =
764435933ddSDimitry Andric File::eOpenOptionWrite | File::eOpenOptionCanCreate;
765ac7ddfbfSEd Maste const bool append = m_outfile_options.GetAppend().GetCurrentValue();
766ac7ddfbfSEd Maste if (append)
767ac7ddfbfSEd Maste open_options |= File::eOpenOptionAppend;
768ac7ddfbfSEd Maste
769*b5893f02SDimitry Andric Status error = FileSystem::Instance().Open(outfile_stream.GetFile(),
770*b5893f02SDimitry Andric outfile_spec, open_options);
771*b5893f02SDimitry Andric if (error.Success()) {
772435933ddSDimitry Andric if (m_memory_options.m_output_as_binary) {
773435933ddSDimitry Andric const size_t bytes_written =
774435933ddSDimitry Andric outfile_stream.Write(data_sp->GetBytes(), bytes_read);
775435933ddSDimitry Andric if (bytes_written > 0) {
776435933ddSDimitry Andric result.GetOutputStream().Printf(
777435933ddSDimitry Andric "%zi bytes %s to '%s'\n", bytes_written,
778*b5893f02SDimitry Andric append ? "appended" : "written", path.c_str());
779ac7ddfbfSEd Maste return true;
780435933ddSDimitry Andric } else {
781435933ddSDimitry Andric result.AppendErrorWithFormat("Failed to write %" PRIu64
782435933ddSDimitry Andric " bytes to '%s'.\n",
783*b5893f02SDimitry Andric (uint64_t)bytes_read, path.c_str());
784ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
785ac7ddfbfSEd Maste return false;
786ac7ddfbfSEd Maste }
787435933ddSDimitry Andric } else {
788ac7ddfbfSEd Maste // We are going to write ASCII to the file just point the
789ac7ddfbfSEd Maste // output_stream to our outfile_stream...
790ac7ddfbfSEd Maste output_stream = &outfile_stream;
791ac7ddfbfSEd Maste }
792435933ddSDimitry Andric } else {
793*b5893f02SDimitry Andric result.AppendErrorWithFormat("Failed to open file '%s' for %s.\n",
794*b5893f02SDimitry Andric path.c_str(), append ? "append" : "write");
795ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
796ac7ddfbfSEd Maste return false;
797ac7ddfbfSEd Maste }
798435933ddSDimitry Andric } else {
799ac7ddfbfSEd Maste output_stream = &result.GetOutputStream();
800ac7ddfbfSEd Maste }
801ac7ddfbfSEd Maste
802ac7ddfbfSEd Maste ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
803435933ddSDimitry Andric if (clang_ast_type.GetOpaqueQualType()) {
804435933ddSDimitry Andric for (uint32_t i = 0; i < item_count; ++i) {
805ac7ddfbfSEd Maste addr_t item_addr = addr + (i * item_byte_size);
806ac7ddfbfSEd Maste Address address(item_addr);
807ac7ddfbfSEd Maste StreamString name_strm;
808ac7ddfbfSEd Maste name_strm.Printf("0x%" PRIx64, item_addr);
809435933ddSDimitry Andric ValueObjectSP valobj_sp(ValueObjectMemory::Create(
810435933ddSDimitry Andric exe_scope, name_strm.GetString(), address, clang_ast_type));
811435933ddSDimitry Andric if (valobj_sp) {
812ac7ddfbfSEd Maste Format format = m_format_options.GetFormat();
813ac7ddfbfSEd Maste if (format != eFormatDefault)
814ac7ddfbfSEd Maste valobj_sp->SetFormat(format);
815ac7ddfbfSEd Maste
816435933ddSDimitry Andric DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
817435933ddSDimitry Andric eLanguageRuntimeDescriptionDisplayVerbosityFull, format));
818ac7ddfbfSEd Maste
81935617911SEd Maste valobj_sp->Dump(*output_stream, options);
820435933ddSDimitry Andric } else {
821435933ddSDimitry Andric result.AppendErrorWithFormat(
822435933ddSDimitry Andric "failed to create a value object for: (%s) %s\n",
823435933ddSDimitry Andric view_as_type_cstr, name_strm.GetData());
824ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
825ac7ddfbfSEd Maste return false;
826ac7ddfbfSEd Maste }
827ac7ddfbfSEd Maste }
828ac7ddfbfSEd Maste return true;
829ac7ddfbfSEd Maste }
830ac7ddfbfSEd Maste
831ac7ddfbfSEd Maste result.SetStatus(eReturnStatusSuccessFinishResult);
832435933ddSDimitry Andric DataExtractor data(data_sp, target->GetArchitecture().GetByteOrder(),
8337aa51b79SEd Maste target->GetArchitecture().GetAddressByteSize(),
8347aa51b79SEd Maste target->GetArchitecture().GetDataByteSize());
835ac7ddfbfSEd Maste
836ac7ddfbfSEd Maste Format format = m_format_options.GetFormat();
837435933ddSDimitry Andric if (((format == eFormatChar) || (format == eFormatCharPrintable)) &&
838435933ddSDimitry Andric (item_byte_size != 1)) {
83935617911SEd Maste // if a count was not passed, or it is 1
840435933ddSDimitry Andric if (!m_format_options.GetCountValue().OptionWasSet() || item_count == 1) {
841ac7ddfbfSEd Maste // this turns requests such as
842ac7ddfbfSEd Maste // memory read -fc -s10 -c1 *charPtrPtr
8434ba319b5SDimitry Andric // which make no sense (what is a char of size 10?) into a request for
8444ba319b5SDimitry Andric // fetching 10 chars of size 1 from the same memory location
845ac7ddfbfSEd Maste format = eFormatCharArray;
846ac7ddfbfSEd Maste item_count = item_byte_size;
847ac7ddfbfSEd Maste item_byte_size = 1;
848435933ddSDimitry Andric } else {
8494ba319b5SDimitry Andric // here we passed a count, and it was not 1 so we have a byte_size and
8504ba319b5SDimitry Andric // a count we could well multiply those, but instead let's just fail
851435933ddSDimitry Andric result.AppendErrorWithFormat(
852435933ddSDimitry Andric "reading memory as characters of size %" PRIu64 " is not supported",
853435933ddSDimitry Andric (uint64_t)item_byte_size);
85435617911SEd Maste result.SetStatus(eReturnStatusFailed);
85535617911SEd Maste return false;
85635617911SEd Maste }
85735617911SEd Maste }
858ac7ddfbfSEd Maste
859ac7ddfbfSEd Maste assert(output_stream);
860f678e45dSDimitry Andric size_t bytes_dumped = DumpDataExtractor(
861f678e45dSDimitry Andric data, output_stream, 0, format, item_byte_size, item_count,
862f678e45dSDimitry Andric num_per_line / target->GetArchitecture().GetDataByteSize(), addr, 0, 0,
863f678e45dSDimitry Andric exe_scope);
864ac7ddfbfSEd Maste m_next_addr = addr + bytes_dumped;
865ac7ddfbfSEd Maste output_stream->EOL();
866ac7ddfbfSEd Maste return true;
867ac7ddfbfSEd Maste }
868ac7ddfbfSEd Maste
869ac7ddfbfSEd Maste OptionGroupOptions m_option_group;
870ac7ddfbfSEd Maste OptionGroupFormat m_format_options;
871ac7ddfbfSEd Maste OptionGroupReadMemory m_memory_options;
872ac7ddfbfSEd Maste OptionGroupOutputFile m_outfile_options;
873ac7ddfbfSEd Maste OptionGroupValueObjectDisplay m_varobj_options;
874ac7ddfbfSEd Maste lldb::addr_t m_next_addr;
875ac7ddfbfSEd Maste lldb::addr_t m_prev_byte_size;
876ac7ddfbfSEd Maste OptionGroupFormat m_prev_format_options;
877ac7ddfbfSEd Maste OptionGroupReadMemory m_prev_memory_options;
878ac7ddfbfSEd Maste OptionGroupOutputFile m_prev_outfile_options;
879ac7ddfbfSEd Maste OptionGroupValueObjectDisplay m_prev_varobj_options;
8809f2f44ceSEd Maste CompilerType m_prev_clang_ast_type;
881ac7ddfbfSEd Maste };
882ac7ddfbfSEd Maste
883*b5893f02SDimitry Andric static constexpr OptionDefinition g_memory_find_option_table[] = {
884435933ddSDimitry Andric // clang-format off
885*b5893f02SDimitry Andric {LLDB_OPT_SET_1, true, "expression", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeExpression, "Evaluate an expression to obtain a byte pattern."},
886*b5893f02SDimitry Andric {LLDB_OPT_SET_2, true, "string", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName, "Use text to find a byte pattern."},
887*b5893f02SDimitry Andric {LLDB_OPT_SET_ALL, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "How many times to perform the search."},
888*b5893f02SDimitry Andric {LLDB_OPT_SET_ALL, false, "dump-offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "When dumping memory for a match, an offset from the match location to start dumping from."},
889435933ddSDimitry Andric // clang-format on
890b952cd58SEd Maste };
891b952cd58SEd Maste
892b952cd58SEd Maste //----------------------------------------------------------------------
893b952cd58SEd Maste // Find the specified data in memory
894b952cd58SEd Maste //----------------------------------------------------------------------
895435933ddSDimitry Andric class CommandObjectMemoryFind : public CommandObjectParsed {
896b952cd58SEd Maste public:
897435933ddSDimitry Andric class OptionGroupFindMemory : public OptionGroup {
898b952cd58SEd Maste public:
OptionGroupFindMemory()899435933ddSDimitry Andric OptionGroupFindMemory() : OptionGroup(), m_count(1), m_offset(0) {}
900b952cd58SEd Maste
9014bb0738eSEd Maste ~OptionGroupFindMemory() override = default;
902b952cd58SEd Maste
GetDefinitions()903435933ddSDimitry Andric llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
904435933ddSDimitry Andric return llvm::makeArrayRef(g_memory_find_option_table);
905b952cd58SEd Maste }
906b952cd58SEd Maste
SetOptionValue(uint32_t option_idx,llvm::StringRef option_value,ExecutionContext * execution_context)9075517e702SDimitry Andric Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
908435933ddSDimitry Andric ExecutionContext *execution_context) override {
9095517e702SDimitry Andric Status error;
910435933ddSDimitry Andric const int short_option =
911435933ddSDimitry Andric g_memory_find_option_table[option_idx].short_option;
912b952cd58SEd Maste
913435933ddSDimitry Andric switch (short_option) {
914b952cd58SEd Maste case 'e':
915435933ddSDimitry Andric m_expr.SetValueFromString(option_value);
916b952cd58SEd Maste break;
917b952cd58SEd Maste
918b952cd58SEd Maste case 's':
919435933ddSDimitry Andric m_string.SetValueFromString(option_value);
920b952cd58SEd Maste break;
921b952cd58SEd Maste
922b952cd58SEd Maste case 'c':
923435933ddSDimitry Andric if (m_count.SetValueFromString(option_value).Fail())
924b952cd58SEd Maste error.SetErrorString("unrecognized value for count");
925b952cd58SEd Maste break;
926b952cd58SEd Maste
927b952cd58SEd Maste case 'o':
928435933ddSDimitry Andric if (m_offset.SetValueFromString(option_value).Fail())
929b952cd58SEd Maste error.SetErrorString("unrecognized value for dump-offset");
930b952cd58SEd Maste break;
931b952cd58SEd Maste
932b952cd58SEd Maste default:
933435933ddSDimitry Andric error.SetErrorStringWithFormat("unrecognized short option '%c'",
934435933ddSDimitry Andric short_option);
935b952cd58SEd Maste break;
936b952cd58SEd Maste }
937b952cd58SEd Maste return error;
938b952cd58SEd Maste }
939b952cd58SEd Maste
OptionParsingStarting(ExecutionContext * execution_context)940435933ddSDimitry Andric void OptionParsingStarting(ExecutionContext *execution_context) override {
941b952cd58SEd Maste m_expr.Clear();
942b952cd58SEd Maste m_string.Clear();
943b952cd58SEd Maste m_count.Clear();
944b952cd58SEd Maste }
945b952cd58SEd Maste
946b952cd58SEd Maste OptionValueString m_expr;
947b952cd58SEd Maste OptionValueString m_string;
948b952cd58SEd Maste OptionValueUInt64 m_count;
949b952cd58SEd Maste OptionValueUInt64 m_offset;
950b952cd58SEd Maste };
951b952cd58SEd Maste
CommandObjectMemoryFind(CommandInterpreter & interpreter)9524bb0738eSEd Maste CommandObjectMemoryFind(CommandInterpreter &interpreter)
953435933ddSDimitry Andric : CommandObjectParsed(
954435933ddSDimitry Andric interpreter, "memory find",
955435933ddSDimitry Andric "Find a value in the memory of the current target process.",
9564bb0738eSEd Maste nullptr, eCommandRequiresProcess | eCommandProcessMustBeLaunched),
957435933ddSDimitry Andric m_option_group(), m_memory_options() {
958b952cd58SEd Maste CommandArgumentEntry arg1;
959b952cd58SEd Maste CommandArgumentEntry arg2;
960b952cd58SEd Maste CommandArgumentData addr_arg;
961b952cd58SEd Maste CommandArgumentData value_arg;
962b952cd58SEd Maste
963b952cd58SEd Maste // Define the first (and only) variant of this arg.
9649f2f44ceSEd Maste addr_arg.arg_type = eArgTypeAddressOrExpression;
965b952cd58SEd Maste addr_arg.arg_repetition = eArgRepeatPlain;
966b952cd58SEd Maste
967435933ddSDimitry Andric // There is only one variant this argument could be; put it into the
968435933ddSDimitry Andric // argument entry.
969b952cd58SEd Maste arg1.push_back(addr_arg);
970b952cd58SEd Maste
971b952cd58SEd Maste // Define the first (and only) variant of this arg.
9729f2f44ceSEd Maste value_arg.arg_type = eArgTypeAddressOrExpression;
9739f2f44ceSEd Maste value_arg.arg_repetition = eArgRepeatPlain;
974b952cd58SEd Maste
975435933ddSDimitry Andric // There is only one variant this argument could be; put it into the
976435933ddSDimitry Andric // argument entry.
977b952cd58SEd Maste arg2.push_back(value_arg);
978b952cd58SEd Maste
979b952cd58SEd Maste // Push the data for the first argument into the m_arguments vector.
980b952cd58SEd Maste m_arguments.push_back(arg1);
981b952cd58SEd Maste m_arguments.push_back(arg2);
982b952cd58SEd Maste
9834bb0738eSEd Maste m_option_group.Append(&m_memory_options);
984b952cd58SEd Maste m_option_group.Finalize();
985b952cd58SEd Maste }
986b952cd58SEd Maste
9874bb0738eSEd Maste ~CommandObjectMemoryFind() override = default;
988b952cd58SEd Maste
GetOptions()989435933ddSDimitry Andric Options *GetOptions() override { return &m_option_group; }
990b952cd58SEd Maste
991b952cd58SEd Maste protected:
992435933ddSDimitry Andric class ProcessMemoryIterator {
993435933ddSDimitry Andric public:
ProcessMemoryIterator(ProcessSP process_sp,lldb::addr_t base)994435933ddSDimitry Andric ProcessMemoryIterator(ProcessSP process_sp, lldb::addr_t base)
995435933ddSDimitry Andric : m_process_sp(process_sp), m_base_addr(base), m_is_valid(true) {
996435933ddSDimitry Andric lldbassert(process_sp.get() != nullptr);
997435933ddSDimitry Andric }
998435933ddSDimitry Andric
IsValid()999435933ddSDimitry Andric bool IsValid() { return m_is_valid; }
1000435933ddSDimitry Andric
operator [](lldb::addr_t offset)1001435933ddSDimitry Andric uint8_t operator[](lldb::addr_t offset) {
1002435933ddSDimitry Andric if (!IsValid())
1003435933ddSDimitry Andric return 0;
1004435933ddSDimitry Andric
1005435933ddSDimitry Andric uint8_t retval = 0;
10065517e702SDimitry Andric Status error;
1007435933ddSDimitry Andric if (0 ==
1008435933ddSDimitry Andric m_process_sp->ReadMemory(m_base_addr + offset, &retval, 1, error)) {
1009435933ddSDimitry Andric m_is_valid = false;
1010435933ddSDimitry Andric return 0;
1011435933ddSDimitry Andric }
1012435933ddSDimitry Andric
1013435933ddSDimitry Andric return retval;
1014435933ddSDimitry Andric }
1015435933ddSDimitry Andric
1016435933ddSDimitry Andric private:
1017435933ddSDimitry Andric ProcessSP m_process_sp;
1018435933ddSDimitry Andric lldb::addr_t m_base_addr;
1019435933ddSDimitry Andric bool m_is_valid;
1020435933ddSDimitry Andric };
DoExecute(Args & command,CommandReturnObject & result)1021435933ddSDimitry Andric bool DoExecute(Args &command, CommandReturnObject &result) override {
1022435933ddSDimitry Andric // No need to check "process" for validity as eCommandRequiresProcess
1023435933ddSDimitry Andric // ensures it is valid
1024b952cd58SEd Maste Process *process = m_exe_ctx.GetProcessPtr();
1025b952cd58SEd Maste
1026b952cd58SEd Maste const size_t argc = command.GetArgumentCount();
1027b952cd58SEd Maste
1028435933ddSDimitry Andric if (argc != 2) {
1029b952cd58SEd Maste result.AppendError("two addresses needed for memory find");
1030b952cd58SEd Maste return false;
1031b952cd58SEd Maste }
1032b952cd58SEd Maste
10335517e702SDimitry Andric Status error;
10344ba319b5SDimitry Andric lldb::addr_t low_addr = OptionArgParser::ToAddress(
10354ba319b5SDimitry Andric &m_exe_ctx, command[0].ref, LLDB_INVALID_ADDRESS, &error);
1036435933ddSDimitry Andric if (low_addr == LLDB_INVALID_ADDRESS || error.Fail()) {
1037b952cd58SEd Maste result.AppendError("invalid low address");
1038b952cd58SEd Maste return false;
1039b952cd58SEd Maste }
10404ba319b5SDimitry Andric lldb::addr_t high_addr = OptionArgParser::ToAddress(
1041435933ddSDimitry Andric &m_exe_ctx, command[1].ref, LLDB_INVALID_ADDRESS, &error);
1042435933ddSDimitry Andric if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) {
10437aa51b79SEd Maste result.AppendError("invalid high address");
1044b952cd58SEd Maste return false;
1045b952cd58SEd Maste }
1046b952cd58SEd Maste
1047435933ddSDimitry Andric if (high_addr <= low_addr) {
1048435933ddSDimitry Andric result.AppendError(
1049435933ddSDimitry Andric "starting address must be smaller than ending address");
1050b952cd58SEd Maste return false;
1051b952cd58SEd Maste }
1052b952cd58SEd Maste
1053b952cd58SEd Maste lldb::addr_t found_location = LLDB_INVALID_ADDRESS;
1054b952cd58SEd Maste
1055b952cd58SEd Maste DataBufferHeap buffer;
1056b952cd58SEd Maste
1057b952cd58SEd Maste if (m_memory_options.m_string.OptionWasSet())
1058435933ddSDimitry Andric buffer.CopyData(m_memory_options.m_string.GetStringValue());
1059435933ddSDimitry Andric else if (m_memory_options.m_expr.OptionWasSet()) {
1060b952cd58SEd Maste StackFrame *frame = m_exe_ctx.GetFramePtr();
1061b952cd58SEd Maste ValueObjectSP result_sp;
1062435933ddSDimitry Andric if ((eExpressionCompleted ==
1063435933ddSDimitry Andric process->GetTarget().EvaluateExpression(
1064435933ddSDimitry Andric m_memory_options.m_expr.GetStringValue(), frame, result_sp)) &&
1065435933ddSDimitry Andric result_sp) {
1066b952cd58SEd Maste uint64_t value = result_sp->GetValueAsUnsigned(0);
1067*b5893f02SDimitry Andric llvm::Optional<uint64_t> size =
1068*b5893f02SDimitry Andric result_sp->GetCompilerType().GetByteSize(nullptr);
1069*b5893f02SDimitry Andric if (!size)
1070*b5893f02SDimitry Andric return false;
1071*b5893f02SDimitry Andric switch (*size) {
1072b952cd58SEd Maste case 1: {
1073b952cd58SEd Maste uint8_t byte = (uint8_t)value;
1074b952cd58SEd Maste buffer.CopyData(&byte, 1);
1075435933ddSDimitry Andric } break;
1076b952cd58SEd Maste case 2: {
1077b952cd58SEd Maste uint16_t word = (uint16_t)value;
1078b952cd58SEd Maste buffer.CopyData(&word, 2);
1079435933ddSDimitry Andric } break;
1080b952cd58SEd Maste case 4: {
1081b952cd58SEd Maste uint32_t lword = (uint32_t)value;
1082b952cd58SEd Maste buffer.CopyData(&lword, 4);
1083435933ddSDimitry Andric } break;
1084b952cd58SEd Maste case 8: {
1085b952cd58SEd Maste buffer.CopyData(&value, 8);
1086435933ddSDimitry Andric } break;
1087b952cd58SEd Maste case 3:
1088b952cd58SEd Maste case 5:
1089b952cd58SEd Maste case 6:
1090b952cd58SEd Maste case 7:
1091b952cd58SEd Maste result.AppendError("unknown type. pass a string instead");
1092b952cd58SEd Maste return false;
1093b952cd58SEd Maste default:
1094435933ddSDimitry Andric result.AppendError(
1095435933ddSDimitry Andric "result size larger than 8 bytes. pass a string instead");
1096b952cd58SEd Maste return false;
1097b952cd58SEd Maste }
1098435933ddSDimitry Andric } else {
1099435933ddSDimitry Andric result.AppendError(
1100435933ddSDimitry Andric "expression evaluation failed. pass a string instead");
1101b952cd58SEd Maste return false;
1102b952cd58SEd Maste }
1103435933ddSDimitry Andric } else {
1104435933ddSDimitry Andric result.AppendError(
1105435933ddSDimitry Andric "please pass either a block of text, or an expression to evaluate.");
1106b952cd58SEd Maste return false;
1107b952cd58SEd Maste }
1108b952cd58SEd Maste
1109b952cd58SEd Maste size_t count = m_memory_options.m_count.GetCurrentValue();
1110b952cd58SEd Maste found_location = low_addr;
1111b952cd58SEd Maste bool ever_found = false;
1112435933ddSDimitry Andric while (count) {
1113435933ddSDimitry Andric found_location = FastSearch(found_location, high_addr, buffer.GetBytes(),
1114435933ddSDimitry Andric buffer.GetByteSize());
1115435933ddSDimitry Andric if (found_location == LLDB_INVALID_ADDRESS) {
1116435933ddSDimitry Andric if (!ever_found) {
11179f2f44ceSEd Maste result.AppendMessage("data not found within the range.\n");
1118b952cd58SEd Maste result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
1119435933ddSDimitry Andric } else
11209f2f44ceSEd Maste result.AppendMessage("no more matches within the range.\n");
1121b952cd58SEd Maste break;
1122b952cd58SEd Maste }
1123435933ddSDimitry Andric result.AppendMessageWithFormat("data found at location: 0x%" PRIx64 "\n",
1124435933ddSDimitry Andric found_location);
1125b952cd58SEd Maste
1126b952cd58SEd Maste DataBufferHeap dumpbuffer(32, 0);
1127435933ddSDimitry Andric process->ReadMemory(
1128435933ddSDimitry Andric found_location + m_memory_options.m_offset.GetCurrentValue(),
1129435933ddSDimitry Andric dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(), error);
1130435933ddSDimitry Andric if (!error.Fail()) {
1131435933ddSDimitry Andric DataExtractor data(dumpbuffer.GetBytes(), dumpbuffer.GetByteSize(),
1132435933ddSDimitry Andric process->GetByteOrder(),
1133435933ddSDimitry Andric process->GetAddressByteSize());
1134f678e45dSDimitry Andric DumpDataExtractor(
1135f678e45dSDimitry Andric data, &result.GetOutputStream(), 0, lldb::eFormatBytesWithASCII, 1,
1136435933ddSDimitry Andric dumpbuffer.GetByteSize(), 16,
1137f678e45dSDimitry Andric found_location + m_memory_options.m_offset.GetCurrentValue(), 0, 0);
1138b952cd58SEd Maste result.GetOutputStream().EOL();
1139b952cd58SEd Maste }
1140b952cd58SEd Maste
1141b952cd58SEd Maste --count;
1142b952cd58SEd Maste found_location++;
1143b952cd58SEd Maste ever_found = true;
1144b952cd58SEd Maste }
1145b952cd58SEd Maste
1146b952cd58SEd Maste result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
1147b952cd58SEd Maste return true;
1148b952cd58SEd Maste }
1149b952cd58SEd Maste
FastSearch(lldb::addr_t low,lldb::addr_t high,uint8_t * buffer,size_t buffer_size)1150435933ddSDimitry Andric lldb::addr_t FastSearch(lldb::addr_t low, lldb::addr_t high, uint8_t *buffer,
1151435933ddSDimitry Andric size_t buffer_size) {
1152435933ddSDimitry Andric const size_t region_size = high - low;
1153435933ddSDimitry Andric
1154435933ddSDimitry Andric if (region_size < buffer_size)
1155b952cd58SEd Maste return LLDB_INVALID_ADDRESS;
1156435933ddSDimitry Andric
1157435933ddSDimitry Andric std::vector<size_t> bad_char_heuristic(256, buffer_size);
1158435933ddSDimitry Andric ProcessSP process_sp = m_exe_ctx.GetProcessSP();
1159435933ddSDimitry Andric ProcessMemoryIterator iterator(process_sp, low);
1160435933ddSDimitry Andric
1161435933ddSDimitry Andric for (size_t idx = 0; idx < buffer_size - 1; idx++) {
1162435933ddSDimitry Andric decltype(bad_char_heuristic)::size_type bcu_idx = buffer[idx];
1163435933ddSDimitry Andric bad_char_heuristic[bcu_idx] = buffer_size - idx - 1;
1164b952cd58SEd Maste }
1165435933ddSDimitry Andric for (size_t s = 0; s <= (region_size - buffer_size);) {
1166435933ddSDimitry Andric int64_t j = buffer_size - 1;
1167435933ddSDimitry Andric while (j >= 0 && buffer[j] == iterator[s + j])
1168435933ddSDimitry Andric j--;
1169435933ddSDimitry Andric if (j < 0)
1170435933ddSDimitry Andric return low + s;
1171435933ddSDimitry Andric else
1172435933ddSDimitry Andric s += bad_char_heuristic[iterator[s + buffer_size - 1]];
1173435933ddSDimitry Andric }
1174435933ddSDimitry Andric
1175b952cd58SEd Maste return LLDB_INVALID_ADDRESS;
1176b952cd58SEd Maste }
1177b952cd58SEd Maste
1178b952cd58SEd Maste OptionGroupOptions m_option_group;
1179b952cd58SEd Maste OptionGroupFindMemory m_memory_options;
1180b952cd58SEd Maste };
1181b952cd58SEd Maste
1182*b5893f02SDimitry Andric static constexpr OptionDefinition g_memory_write_option_table[] = {
1183435933ddSDimitry Andric // clang-format off
1184*b5893f02SDimitry Andric {LLDB_OPT_SET_1, true, "infile", 'i', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFilename, "Write memory using the contents of a file."},
1185*b5893f02SDimitry Andric {LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeOffset, "Start writing bytes from an offset within the input file."},
1186435933ddSDimitry Andric // clang-format on
1187ac7ddfbfSEd Maste };
1188ac7ddfbfSEd Maste
1189ac7ddfbfSEd Maste //----------------------------------------------------------------------
1190ac7ddfbfSEd Maste // Write memory to the inferior process
1191ac7ddfbfSEd Maste //----------------------------------------------------------------------
1192435933ddSDimitry Andric class CommandObjectMemoryWrite : public CommandObjectParsed {
1193ac7ddfbfSEd Maste public:
1194435933ddSDimitry Andric class OptionGroupWriteMemory : public OptionGroup {
1195ac7ddfbfSEd Maste public:
OptionGroupWriteMemory()1196435933ddSDimitry Andric OptionGroupWriteMemory() : OptionGroup() {}
1197ac7ddfbfSEd Maste
11984bb0738eSEd Maste ~OptionGroupWriteMemory() override = default;
1199ac7ddfbfSEd Maste
GetDefinitions()1200435933ddSDimitry Andric llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
1201435933ddSDimitry Andric return llvm::makeArrayRef(g_memory_write_option_table);
1202ac7ddfbfSEd Maste }
1203ac7ddfbfSEd Maste
SetOptionValue(uint32_t option_idx,llvm::StringRef option_value,ExecutionContext * execution_context)12045517e702SDimitry Andric Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
1205435933ddSDimitry Andric ExecutionContext *execution_context) override {
12065517e702SDimitry Andric Status error;
1207435933ddSDimitry Andric const int short_option =
1208435933ddSDimitry Andric g_memory_write_option_table[option_idx].short_option;
1209ac7ddfbfSEd Maste
1210435933ddSDimitry Andric switch (short_option) {
1211ac7ddfbfSEd Maste case 'i':
1212*b5893f02SDimitry Andric m_infile.SetFile(option_value, FileSpec::Style::native);
1213*b5893f02SDimitry Andric FileSystem::Instance().Resolve(m_infile);
1214*b5893f02SDimitry Andric if (!FileSystem::Instance().Exists(m_infile)) {
1215ac7ddfbfSEd Maste m_infile.Clear();
1216435933ddSDimitry Andric error.SetErrorStringWithFormat("input file does not exist: '%s'",
1217435933ddSDimitry Andric option_value.str().c_str());
1218ac7ddfbfSEd Maste }
1219ac7ddfbfSEd Maste break;
1220ac7ddfbfSEd Maste
1221435933ddSDimitry Andric case 'o': {
1222435933ddSDimitry Andric if (option_value.getAsInteger(0, m_infile_offset)) {
1223435933ddSDimitry Andric m_infile_offset = 0;
1224435933ddSDimitry Andric error.SetErrorStringWithFormat("invalid offset string '%s'",
1225435933ddSDimitry Andric option_value.str().c_str());
1226ac7ddfbfSEd Maste }
1227435933ddSDimitry Andric } break;
1228ac7ddfbfSEd Maste
1229ac7ddfbfSEd Maste default:
1230435933ddSDimitry Andric error.SetErrorStringWithFormat("unrecognized short option '%c'",
1231435933ddSDimitry Andric short_option);
1232ac7ddfbfSEd Maste break;
1233ac7ddfbfSEd Maste }
1234ac7ddfbfSEd Maste return error;
1235ac7ddfbfSEd Maste }
1236ac7ddfbfSEd Maste
OptionParsingStarting(ExecutionContext * execution_context)1237435933ddSDimitry Andric void OptionParsingStarting(ExecutionContext *execution_context) override {
1238ac7ddfbfSEd Maste m_infile.Clear();
1239ac7ddfbfSEd Maste m_infile_offset = 0;
1240ac7ddfbfSEd Maste }
1241ac7ddfbfSEd Maste
1242ac7ddfbfSEd Maste FileSpec m_infile;
1243ac7ddfbfSEd Maste off_t m_infile_offset;
1244ac7ddfbfSEd Maste };
1245ac7ddfbfSEd Maste
CommandObjectMemoryWrite(CommandInterpreter & interpreter)12464bb0738eSEd Maste CommandObjectMemoryWrite(CommandInterpreter &interpreter)
1247435933ddSDimitry Andric : CommandObjectParsed(
1248435933ddSDimitry Andric interpreter, "memory write",
1249435933ddSDimitry Andric "Write to the memory of the current target process.", nullptr,
1250435933ddSDimitry Andric eCommandRequiresProcess | eCommandProcessMustBeLaunched),
1251435933ddSDimitry Andric m_option_group(), m_format_options(eFormatBytes, 1, UINT64_MAX),
1252435933ddSDimitry Andric m_memory_options() {
1253ac7ddfbfSEd Maste CommandArgumentEntry arg1;
1254ac7ddfbfSEd Maste CommandArgumentEntry arg2;
1255ac7ddfbfSEd Maste CommandArgumentData addr_arg;
1256ac7ddfbfSEd Maste CommandArgumentData value_arg;
1257ac7ddfbfSEd Maste
1258ac7ddfbfSEd Maste // Define the first (and only) variant of this arg.
1259ac7ddfbfSEd Maste addr_arg.arg_type = eArgTypeAddress;
1260ac7ddfbfSEd Maste addr_arg.arg_repetition = eArgRepeatPlain;
1261ac7ddfbfSEd Maste
1262435933ddSDimitry Andric // There is only one variant this argument could be; put it into the
1263435933ddSDimitry Andric // argument entry.
1264ac7ddfbfSEd Maste arg1.push_back(addr_arg);
1265ac7ddfbfSEd Maste
1266ac7ddfbfSEd Maste // Define the first (and only) variant of this arg.
1267ac7ddfbfSEd Maste value_arg.arg_type = eArgTypeValue;
1268ac7ddfbfSEd Maste value_arg.arg_repetition = eArgRepeatPlus;
1269ac7ddfbfSEd Maste
1270435933ddSDimitry Andric // There is only one variant this argument could be; put it into the
1271435933ddSDimitry Andric // argument entry.
1272ac7ddfbfSEd Maste arg2.push_back(value_arg);
1273ac7ddfbfSEd Maste
1274ac7ddfbfSEd Maste // Push the data for the first argument into the m_arguments vector.
1275ac7ddfbfSEd Maste m_arguments.push_back(arg1);
1276ac7ddfbfSEd Maste m_arguments.push_back(arg2);
1277ac7ddfbfSEd Maste
1278435933ddSDimitry Andric m_option_group.Append(&m_format_options,
1279435933ddSDimitry Andric OptionGroupFormat::OPTION_GROUP_FORMAT,
1280435933ddSDimitry Andric LLDB_OPT_SET_1);
1281435933ddSDimitry Andric m_option_group.Append(&m_format_options,
1282435933ddSDimitry Andric OptionGroupFormat::OPTION_GROUP_SIZE,
1283435933ddSDimitry Andric LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
1284ac7ddfbfSEd Maste m_option_group.Append(&m_memory_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_2);
1285ac7ddfbfSEd Maste m_option_group.Finalize();
1286ac7ddfbfSEd Maste }
1287ac7ddfbfSEd Maste
12884bb0738eSEd Maste ~CommandObjectMemoryWrite() override = default;
1289ac7ddfbfSEd Maste
GetOptions()1290435933ddSDimitry Andric Options *GetOptions() override { return &m_option_group; }
1291ac7ddfbfSEd Maste
UIntValueIsValidForSize(uint64_t uval64,size_t total_byte_size)1292435933ddSDimitry Andric bool UIntValueIsValidForSize(uint64_t uval64, size_t total_byte_size) {
1293ac7ddfbfSEd Maste if (total_byte_size > 8)
1294ac7ddfbfSEd Maste return false;
1295ac7ddfbfSEd Maste
1296ac7ddfbfSEd Maste if (total_byte_size == 8)
1297ac7ddfbfSEd Maste return true;
1298ac7ddfbfSEd Maste
1299ac7ddfbfSEd Maste const uint64_t max = ((uint64_t)1 << (uint64_t)(total_byte_size * 8)) - 1;
1300ac7ddfbfSEd Maste return uval64 <= max;
1301ac7ddfbfSEd Maste }
1302ac7ddfbfSEd Maste
SIntValueIsValidForSize(int64_t sval64,size_t total_byte_size)1303435933ddSDimitry Andric bool SIntValueIsValidForSize(int64_t sval64, size_t total_byte_size) {
1304ac7ddfbfSEd Maste if (total_byte_size > 8)
1305ac7ddfbfSEd Maste return false;
1306ac7ddfbfSEd Maste
1307ac7ddfbfSEd Maste if (total_byte_size == 8)
1308ac7ddfbfSEd Maste return true;
1309ac7ddfbfSEd Maste
1310ac7ddfbfSEd Maste const int64_t max = ((int64_t)1 << (uint64_t)(total_byte_size * 8 - 1)) - 1;
1311ac7ddfbfSEd Maste const int64_t min = ~(max);
1312ac7ddfbfSEd Maste return min <= sval64 && sval64 <= max;
1313ac7ddfbfSEd Maste }
1314ac7ddfbfSEd Maste
1315ac7ddfbfSEd Maste protected:
DoExecute(Args & command,CommandReturnObject & result)1316435933ddSDimitry Andric bool DoExecute(Args &command, CommandReturnObject &result) override {
1317435933ddSDimitry Andric // No need to check "process" for validity as eCommandRequiresProcess
1318435933ddSDimitry Andric // ensures it is valid
1319ac7ddfbfSEd Maste Process *process = m_exe_ctx.GetProcessPtr();
1320ac7ddfbfSEd Maste
1321ac7ddfbfSEd Maste const size_t argc = command.GetArgumentCount();
1322ac7ddfbfSEd Maste
1323435933ddSDimitry Andric if (m_memory_options.m_infile) {
1324435933ddSDimitry Andric if (argc < 1) {
1325435933ddSDimitry Andric result.AppendErrorWithFormat(
1326435933ddSDimitry Andric "%s takes a destination address when writing file contents.\n",
1327435933ddSDimitry Andric m_cmd_name.c_str());
1328ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1329ac7ddfbfSEd Maste return false;
1330ac7ddfbfSEd Maste }
1331435933ddSDimitry Andric } else if (argc < 2) {
1332435933ddSDimitry Andric result.AppendErrorWithFormat(
1333435933ddSDimitry Andric "%s takes a destination address and at least one value.\n",
1334435933ddSDimitry Andric m_cmd_name.c_str());
1335ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1336ac7ddfbfSEd Maste return false;
1337ac7ddfbfSEd Maste }
1338ac7ddfbfSEd Maste
1339435933ddSDimitry Andric StreamString buffer(
1340435933ddSDimitry Andric Stream::eBinary,
1341ac7ddfbfSEd Maste process->GetTarget().GetArchitecture().GetAddressByteSize(),
1342ac7ddfbfSEd Maste process->GetTarget().GetArchitecture().GetByteOrder());
1343ac7ddfbfSEd Maste
1344ac7ddfbfSEd Maste OptionValueUInt64 &byte_size_value = m_format_options.GetByteSizeValue();
1345ac7ddfbfSEd Maste size_t item_byte_size = byte_size_value.GetCurrentValue();
1346ac7ddfbfSEd Maste
13475517e702SDimitry Andric Status error;
13484ba319b5SDimitry Andric lldb::addr_t addr = OptionArgParser::ToAddress(
13494ba319b5SDimitry Andric &m_exe_ctx, command[0].ref, LLDB_INVALID_ADDRESS, &error);
1350ac7ddfbfSEd Maste
1351435933ddSDimitry Andric if (addr == LLDB_INVALID_ADDRESS) {
1352ac7ddfbfSEd Maste result.AppendError("invalid address expression\n");
1353ac7ddfbfSEd Maste result.AppendError(error.AsCString());
1354ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1355ac7ddfbfSEd Maste return false;
1356ac7ddfbfSEd Maste }
1357ac7ddfbfSEd Maste
1358435933ddSDimitry Andric if (m_memory_options.m_infile) {
1359ac7ddfbfSEd Maste size_t length = SIZE_MAX;
13601c3bbb01SEd Maste if (item_byte_size > 1)
1361ac7ddfbfSEd Maste length = item_byte_size;
1362*b5893f02SDimitry Andric auto data_sp = FileSystem::Instance().CreateDataBuffer(
1363f678e45dSDimitry Andric m_memory_options.m_infile.GetPath(), length,
1364f678e45dSDimitry Andric m_memory_options.m_infile_offset);
1365435933ddSDimitry Andric if (data_sp) {
1366ac7ddfbfSEd Maste length = data_sp->GetByteSize();
1367435933ddSDimitry Andric if (length > 0) {
13685517e702SDimitry Andric Status error;
1369435933ddSDimitry Andric size_t bytes_written =
1370435933ddSDimitry Andric process->WriteMemory(addr, data_sp->GetBytes(), length, error);
1371ac7ddfbfSEd Maste
1372435933ddSDimitry Andric if (bytes_written == length) {
1373ac7ddfbfSEd Maste // All bytes written
1374435933ddSDimitry Andric result.GetOutputStream().Printf(
1375435933ddSDimitry Andric "%" PRIu64 " bytes were written to 0x%" PRIx64 "\n",
1376435933ddSDimitry Andric (uint64_t)bytes_written, addr);
1377ac7ddfbfSEd Maste result.SetStatus(eReturnStatusSuccessFinishResult);
1378435933ddSDimitry Andric } else if (bytes_written > 0) {
1379ac7ddfbfSEd Maste // Some byte written
1380435933ddSDimitry Andric result.GetOutputStream().Printf(
1381435933ddSDimitry Andric "%" PRIu64 " bytes of %" PRIu64
1382435933ddSDimitry Andric " requested were written to 0x%" PRIx64 "\n",
1383435933ddSDimitry Andric (uint64_t)bytes_written, (uint64_t)length, addr);
1384ac7ddfbfSEd Maste result.SetStatus(eReturnStatusSuccessFinishResult);
1385435933ddSDimitry Andric } else {
1386435933ddSDimitry Andric result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
1387435933ddSDimitry Andric " failed: %s.\n",
1388435933ddSDimitry Andric addr, error.AsCString());
1389ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1390ac7ddfbfSEd Maste }
1391ac7ddfbfSEd Maste }
1392435933ddSDimitry Andric } else {
1393ac7ddfbfSEd Maste result.AppendErrorWithFormat("Unable to read contents of file.\n");
1394ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1395ac7ddfbfSEd Maste }
1396ac7ddfbfSEd Maste return result.Succeeded();
1397435933ddSDimitry Andric } else if (item_byte_size == 0) {
1398ac7ddfbfSEd Maste if (m_format_options.GetFormat() == eFormatPointer)
1399ac7ddfbfSEd Maste item_byte_size = buffer.GetAddressByteSize();
1400ac7ddfbfSEd Maste else
1401ac7ddfbfSEd Maste item_byte_size = 1;
1402ac7ddfbfSEd Maste }
1403ac7ddfbfSEd Maste
1404ac7ddfbfSEd Maste command.Shift(); // shift off the address argument
1405ac7ddfbfSEd Maste uint64_t uval64;
1406ac7ddfbfSEd Maste int64_t sval64;
1407ac7ddfbfSEd Maste bool success = false;
1408435933ddSDimitry Andric for (auto &entry : command) {
1409435933ddSDimitry Andric switch (m_format_options.GetFormat()) {
1410ac7ddfbfSEd Maste case kNumFormats:
1411ac7ddfbfSEd Maste case eFormatFloat: // TODO: add support for floats soon
1412ac7ddfbfSEd Maste case eFormatCharPrintable:
1413ac7ddfbfSEd Maste case eFormatBytesWithASCII:
1414ac7ddfbfSEd Maste case eFormatComplex:
1415ac7ddfbfSEd Maste case eFormatEnum:
1416ac7ddfbfSEd Maste case eFormatUnicode16:
1417ac7ddfbfSEd Maste case eFormatUnicode32:
1418ac7ddfbfSEd Maste case eFormatVectorOfChar:
1419ac7ddfbfSEd Maste case eFormatVectorOfSInt8:
1420ac7ddfbfSEd Maste case eFormatVectorOfUInt8:
1421ac7ddfbfSEd Maste case eFormatVectorOfSInt16:
1422ac7ddfbfSEd Maste case eFormatVectorOfUInt16:
1423ac7ddfbfSEd Maste case eFormatVectorOfSInt32:
1424ac7ddfbfSEd Maste case eFormatVectorOfUInt32:
1425ac7ddfbfSEd Maste case eFormatVectorOfSInt64:
1426ac7ddfbfSEd Maste case eFormatVectorOfUInt64:
14279f2f44ceSEd Maste case eFormatVectorOfFloat16:
1428ac7ddfbfSEd Maste case eFormatVectorOfFloat32:
1429ac7ddfbfSEd Maste case eFormatVectorOfFloat64:
1430ac7ddfbfSEd Maste case eFormatVectorOfUInt128:
1431ac7ddfbfSEd Maste case eFormatOSType:
1432ac7ddfbfSEd Maste case eFormatComplexInteger:
1433ac7ddfbfSEd Maste case eFormatAddressInfo:
1434ac7ddfbfSEd Maste case eFormatHexFloat:
1435ac7ddfbfSEd Maste case eFormatInstruction:
1436ac7ddfbfSEd Maste case eFormatVoid:
1437ac7ddfbfSEd Maste result.AppendError("unsupported format for writing memory");
1438ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1439ac7ddfbfSEd Maste return false;
1440ac7ddfbfSEd Maste
1441ac7ddfbfSEd Maste case eFormatDefault:
1442ac7ddfbfSEd Maste case eFormatBytes:
1443ac7ddfbfSEd Maste case eFormatHex:
1444ac7ddfbfSEd Maste case eFormatHexUppercase:
1445ac7ddfbfSEd Maste case eFormatPointer:
1446f678e45dSDimitry Andric {
1447ac7ddfbfSEd Maste // Decode hex bytes
1448f678e45dSDimitry Andric // Be careful, getAsInteger with a radix of 16 rejects "0xab" so we
1449f678e45dSDimitry Andric // have to special case that:
1450f678e45dSDimitry Andric bool success = false;
1451f678e45dSDimitry Andric if (entry.ref.startswith("0x"))
1452f678e45dSDimitry Andric success = !entry.ref.getAsInteger(0, uval64);
1453f678e45dSDimitry Andric if (!success)
1454f678e45dSDimitry Andric success = !entry.ref.getAsInteger(16, uval64);
1455f678e45dSDimitry Andric if (!success) {
1456435933ddSDimitry Andric result.AppendErrorWithFormat(
1457435933ddSDimitry Andric "'%s' is not a valid hex string value.\n", entry.c_str());
1458ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1459ac7ddfbfSEd Maste return false;
1460435933ddSDimitry Andric } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) {
1461435933ddSDimitry Andric result.AppendErrorWithFormat("Value 0x%" PRIx64
1462435933ddSDimitry Andric " is too large to fit in a %" PRIu64
1463435933ddSDimitry Andric " byte unsigned integer value.\n",
1464435933ddSDimitry Andric uval64, (uint64_t)item_byte_size);
1465ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1466ac7ddfbfSEd Maste return false;
1467ac7ddfbfSEd Maste }
1468ac7ddfbfSEd Maste buffer.PutMaxHex64(uval64, item_byte_size);
1469ac7ddfbfSEd Maste break;
1470f678e45dSDimitry Andric }
1471ac7ddfbfSEd Maste case eFormatBoolean:
14724ba319b5SDimitry Andric uval64 = OptionArgParser::ToBoolean(entry.ref, false, &success);
1473435933ddSDimitry Andric if (!success) {
1474435933ddSDimitry Andric result.AppendErrorWithFormat(
1475435933ddSDimitry Andric "'%s' is not a valid boolean string value.\n", entry.c_str());
1476ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1477ac7ddfbfSEd Maste return false;
1478ac7ddfbfSEd Maste }
1479ac7ddfbfSEd Maste buffer.PutMaxHex64(uval64, item_byte_size);
1480ac7ddfbfSEd Maste break;
1481ac7ddfbfSEd Maste
1482ac7ddfbfSEd Maste case eFormatBinary:
1483435933ddSDimitry Andric if (entry.ref.getAsInteger(2, uval64)) {
1484435933ddSDimitry Andric result.AppendErrorWithFormat(
1485435933ddSDimitry Andric "'%s' is not a valid binary string value.\n", entry.c_str());
1486ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1487ac7ddfbfSEd Maste return false;
1488435933ddSDimitry Andric } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) {
1489435933ddSDimitry Andric result.AppendErrorWithFormat("Value 0x%" PRIx64
1490435933ddSDimitry Andric " is too large to fit in a %" PRIu64
1491435933ddSDimitry Andric " byte unsigned integer value.\n",
1492435933ddSDimitry Andric uval64, (uint64_t)item_byte_size);
1493ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1494ac7ddfbfSEd Maste return false;
1495ac7ddfbfSEd Maste }
1496ac7ddfbfSEd Maste buffer.PutMaxHex64(uval64, item_byte_size);
1497ac7ddfbfSEd Maste break;
1498ac7ddfbfSEd Maste
1499ac7ddfbfSEd Maste case eFormatCharArray:
1500ac7ddfbfSEd Maste case eFormatChar:
1501435933ddSDimitry Andric case eFormatCString: {
1502435933ddSDimitry Andric if (entry.ref.empty())
1503435933ddSDimitry Andric break;
1504435933ddSDimitry Andric
1505435933ddSDimitry Andric size_t len = entry.ref.size();
1506ac7ddfbfSEd Maste // Include the NULL for C strings...
1507ac7ddfbfSEd Maste if (m_format_options.GetFormat() == eFormatCString)
1508ac7ddfbfSEd Maste ++len;
15095517e702SDimitry Andric Status error;
1510435933ddSDimitry Andric if (process->WriteMemory(addr, entry.c_str(), len, error) == len) {
1511ac7ddfbfSEd Maste addr += len;
1512435933ddSDimitry Andric } else {
1513435933ddSDimitry Andric result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
1514435933ddSDimitry Andric " failed: %s.\n",
1515435933ddSDimitry Andric addr, error.AsCString());
1516ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1517ac7ddfbfSEd Maste return false;
1518ac7ddfbfSEd Maste }
1519ac7ddfbfSEd Maste break;
1520435933ddSDimitry Andric }
1521ac7ddfbfSEd Maste case eFormatDecimal:
1522435933ddSDimitry Andric if (entry.ref.getAsInteger(0, sval64)) {
1523435933ddSDimitry Andric result.AppendErrorWithFormat(
1524435933ddSDimitry Andric "'%s' is not a valid signed decimal value.\n", entry.c_str());
1525ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1526ac7ddfbfSEd Maste return false;
1527435933ddSDimitry Andric } else if (!SIntValueIsValidForSize(sval64, item_byte_size)) {
1528435933ddSDimitry Andric result.AppendErrorWithFormat(
1529435933ddSDimitry Andric "Value %" PRIi64 " is too large or small to fit in a %" PRIu64
1530435933ddSDimitry Andric " byte signed integer value.\n",
1531435933ddSDimitry Andric sval64, (uint64_t)item_byte_size);
1532ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1533ac7ddfbfSEd Maste return false;
1534ac7ddfbfSEd Maste }
1535ac7ddfbfSEd Maste buffer.PutMaxHex64(sval64, item_byte_size);
1536ac7ddfbfSEd Maste break;
1537ac7ddfbfSEd Maste
1538ac7ddfbfSEd Maste case eFormatUnsigned:
1539435933ddSDimitry Andric
1540435933ddSDimitry Andric if (!entry.ref.getAsInteger(0, uval64)) {
1541435933ddSDimitry Andric result.AppendErrorWithFormat(
1542435933ddSDimitry Andric "'%s' is not a valid unsigned decimal string value.\n",
1543435933ddSDimitry Andric entry.c_str());
1544ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1545ac7ddfbfSEd Maste return false;
1546435933ddSDimitry Andric } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) {
1547435933ddSDimitry Andric result.AppendErrorWithFormat("Value %" PRIu64
1548435933ddSDimitry Andric " is too large to fit in a %" PRIu64
1549435933ddSDimitry Andric " byte unsigned integer value.\n",
1550435933ddSDimitry Andric uval64, (uint64_t)item_byte_size);
1551ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1552ac7ddfbfSEd Maste return false;
1553ac7ddfbfSEd Maste }
1554ac7ddfbfSEd Maste buffer.PutMaxHex64(uval64, item_byte_size);
1555ac7ddfbfSEd Maste break;
1556ac7ddfbfSEd Maste
1557ac7ddfbfSEd Maste case eFormatOctal:
1558435933ddSDimitry Andric if (entry.ref.getAsInteger(8, uval64)) {
1559435933ddSDimitry Andric result.AppendErrorWithFormat(
1560435933ddSDimitry Andric "'%s' is not a valid octal string value.\n", entry.c_str());
1561ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1562ac7ddfbfSEd Maste return false;
1563435933ddSDimitry Andric } else if (!UIntValueIsValidForSize(uval64, item_byte_size)) {
1564435933ddSDimitry Andric result.AppendErrorWithFormat("Value %" PRIo64
1565435933ddSDimitry Andric " is too large to fit in a %" PRIu64
1566435933ddSDimitry Andric " byte unsigned integer value.\n",
1567435933ddSDimitry Andric uval64, (uint64_t)item_byte_size);
1568ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1569ac7ddfbfSEd Maste return false;
1570ac7ddfbfSEd Maste }
1571ac7ddfbfSEd Maste buffer.PutMaxHex64(uval64, item_byte_size);
1572ac7ddfbfSEd Maste break;
1573ac7ddfbfSEd Maste }
1574ac7ddfbfSEd Maste }
1575ac7ddfbfSEd Maste
1576435933ddSDimitry Andric if (!buffer.GetString().empty()) {
15775517e702SDimitry Andric Status error;
1578435933ddSDimitry Andric if (process->WriteMemory(addr, buffer.GetString().data(),
1579435933ddSDimitry Andric buffer.GetString().size(),
1580435933ddSDimitry Andric error) == buffer.GetString().size())
1581ac7ddfbfSEd Maste return true;
1582435933ddSDimitry Andric else {
1583435933ddSDimitry Andric result.AppendErrorWithFormat("Memory write to 0x%" PRIx64
1584435933ddSDimitry Andric " failed: %s.\n",
1585435933ddSDimitry Andric addr, error.AsCString());
1586ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
1587ac7ddfbfSEd Maste return false;
1588ac7ddfbfSEd Maste }
1589ac7ddfbfSEd Maste }
1590ac7ddfbfSEd Maste return true;
1591ac7ddfbfSEd Maste }
1592ac7ddfbfSEd Maste
1593ac7ddfbfSEd Maste OptionGroupOptions m_option_group;
1594ac7ddfbfSEd Maste OptionGroupFormat m_format_options;
1595ac7ddfbfSEd Maste OptionGroupWriteMemory m_memory_options;
1596ac7ddfbfSEd Maste };
1597ac7ddfbfSEd Maste
15987aa51b79SEd Maste //----------------------------------------------------------------------
15997aa51b79SEd Maste // Get malloc/free history of a memory address.
16007aa51b79SEd Maste //----------------------------------------------------------------------
1601435933ddSDimitry Andric class CommandObjectMemoryHistory : public CommandObjectParsed {
16027aa51b79SEd Maste public:
CommandObjectMemoryHistory(CommandInterpreter & interpreter)16034bb0738eSEd Maste CommandObjectMemoryHistory(CommandInterpreter &interpreter)
16044bb0738eSEd Maste : CommandObjectParsed(
1605435933ddSDimitry Andric interpreter, "memory history", "Print recorded stack traces for "
1606435933ddSDimitry Andric "allocation/deallocation events "
1607435933ddSDimitry Andric "associated with an address.",
1608435933ddSDimitry Andric nullptr,
1609435933ddSDimitry Andric eCommandRequiresTarget | eCommandRequiresProcess |
1610435933ddSDimitry Andric eCommandProcessMustBePaused | eCommandProcessMustBeLaunched) {
16117aa51b79SEd Maste CommandArgumentEntry arg1;
16127aa51b79SEd Maste CommandArgumentData addr_arg;
16137aa51b79SEd Maste
16147aa51b79SEd Maste // Define the first (and only) variant of this arg.
16157aa51b79SEd Maste addr_arg.arg_type = eArgTypeAddress;
16167aa51b79SEd Maste addr_arg.arg_repetition = eArgRepeatPlain;
16177aa51b79SEd Maste
1618435933ddSDimitry Andric // There is only one variant this argument could be; put it into the
1619435933ddSDimitry Andric // argument entry.
16207aa51b79SEd Maste arg1.push_back(addr_arg);
16217aa51b79SEd Maste
16227aa51b79SEd Maste // Push the data for the first argument into the m_arguments vector.
16237aa51b79SEd Maste m_arguments.push_back(arg1);
16247aa51b79SEd Maste }
16257aa51b79SEd Maste
16264bb0738eSEd Maste ~CommandObjectMemoryHistory() override = default;
16277aa51b79SEd Maste
GetRepeatCommand(Args & current_command_args,uint32_t index)1628435933ddSDimitry Andric const char *GetRepeatCommand(Args ¤t_command_args,
1629435933ddSDimitry Andric uint32_t index) override {
16307aa51b79SEd Maste return m_cmd_name.c_str();
16317aa51b79SEd Maste }
16327aa51b79SEd Maste
16337aa51b79SEd Maste protected:
DoExecute(Args & command,CommandReturnObject & result)1634435933ddSDimitry Andric bool DoExecute(Args &command, CommandReturnObject &result) override {
16357aa51b79SEd Maste const size_t argc = command.GetArgumentCount();
16367aa51b79SEd Maste
1637435933ddSDimitry Andric if (argc == 0 || argc > 1) {
1638435933ddSDimitry Andric result.AppendErrorWithFormat("%s takes an address expression",
1639435933ddSDimitry Andric m_cmd_name.c_str());
16407aa51b79SEd Maste result.SetStatus(eReturnStatusFailed);
16417aa51b79SEd Maste return false;
16427aa51b79SEd Maste }
16437aa51b79SEd Maste
16445517e702SDimitry Andric Status error;
16454ba319b5SDimitry Andric lldb::addr_t addr = OptionArgParser::ToAddress(
16464ba319b5SDimitry Andric &m_exe_ctx, command[0].ref, LLDB_INVALID_ADDRESS, &error);
16477aa51b79SEd Maste
1648435933ddSDimitry Andric if (addr == LLDB_INVALID_ADDRESS) {
16497aa51b79SEd Maste result.AppendError("invalid address expression");
16507aa51b79SEd Maste result.AppendError(error.AsCString());
16517aa51b79SEd Maste result.SetStatus(eReturnStatusFailed);
16527aa51b79SEd Maste return false;
16537aa51b79SEd Maste }
16547aa51b79SEd Maste
16557aa51b79SEd Maste Stream *output_stream = &result.GetOutputStream();
16567aa51b79SEd Maste
16577aa51b79SEd Maste const ProcessSP &process_sp = m_exe_ctx.GetProcessSP();
1658435933ddSDimitry Andric const MemoryHistorySP &memory_history =
1659435933ddSDimitry Andric MemoryHistory::FindPlugin(process_sp);
16607aa51b79SEd Maste
1661435933ddSDimitry Andric if (!memory_history) {
16627aa51b79SEd Maste result.AppendError("no available memory history provider");
16637aa51b79SEd Maste result.SetStatus(eReturnStatusFailed);
16647aa51b79SEd Maste return false;
16657aa51b79SEd Maste }
16667aa51b79SEd Maste
16677aa51b79SEd Maste HistoryThreads thread_list = memory_history->GetHistoryThreads(addr);
16687aa51b79SEd Maste
1669435933ddSDimitry Andric const bool stop_format = false;
16707aa51b79SEd Maste for (auto thread : thread_list) {
1671435933ddSDimitry Andric thread->GetStatus(*output_stream, 0, UINT32_MAX, 0, stop_format);
16727aa51b79SEd Maste }
16737aa51b79SEd Maste
16747aa51b79SEd Maste result.SetStatus(eReturnStatusSuccessFinishResult);
16757aa51b79SEd Maste
16767aa51b79SEd Maste return true;
16777aa51b79SEd Maste }
16787aa51b79SEd Maste };
16797aa51b79SEd Maste
16804bb0738eSEd Maste //-------------------------------------------------------------------------
16814bb0738eSEd Maste // CommandObjectMemoryRegion
16824bb0738eSEd Maste //-------------------------------------------------------------------------
16834bb0738eSEd Maste #pragma mark CommandObjectMemoryRegion
16844bb0738eSEd Maste
1685435933ddSDimitry Andric class CommandObjectMemoryRegion : public CommandObjectParsed {
16864bb0738eSEd Maste public:
CommandObjectMemoryRegion(CommandInterpreter & interpreter)16874bb0738eSEd Maste CommandObjectMemoryRegion(CommandInterpreter &interpreter)
1688435933ddSDimitry Andric : CommandObjectParsed(interpreter, "memory region",
1689435933ddSDimitry Andric "Get information on the memory region containing "
1690435933ddSDimitry Andric "an address in the current target process.",
1691435933ddSDimitry Andric "memory region ADDR",
1692435933ddSDimitry Andric eCommandRequiresProcess | eCommandTryTargetAPILock |
1693435933ddSDimitry Andric eCommandProcessMustBeLaunched),
1694435933ddSDimitry Andric m_prev_end_addr(LLDB_INVALID_ADDRESS) {}
16954bb0738eSEd Maste
16964bb0738eSEd Maste ~CommandObjectMemoryRegion() override = default;
16974bb0738eSEd Maste
16984bb0738eSEd Maste protected:
DoExecute(Args & command,CommandReturnObject & result)1699435933ddSDimitry Andric bool DoExecute(Args &command, CommandReturnObject &result) override {
17004bb0738eSEd Maste ProcessSP process_sp = m_exe_ctx.GetProcessSP();
1701435933ddSDimitry Andric if (process_sp) {
17025517e702SDimitry Andric Status error;
17034bb0738eSEd Maste lldb::addr_t load_addr = m_prev_end_addr;
17044bb0738eSEd Maste m_prev_end_addr = LLDB_INVALID_ADDRESS;
17054bb0738eSEd Maste
17064bb0738eSEd Maste const size_t argc = command.GetArgumentCount();
1707435933ddSDimitry Andric if (argc > 1 || (argc == 0 && load_addr == LLDB_INVALID_ADDRESS)) {
1708435933ddSDimitry Andric result.AppendErrorWithFormat("'%s' takes one argument:\nUsage: %s\n",
1709435933ddSDimitry Andric m_cmd_name.c_str(), m_cmd_syntax.c_str());
17104bb0738eSEd Maste result.SetStatus(eReturnStatusFailed);
1711435933ddSDimitry Andric } else {
1712435933ddSDimitry Andric if (command.GetArgumentCount() == 1) {
17134ba319b5SDimitry Andric auto load_addr_str = command[0].ref;
17144ba319b5SDimitry Andric load_addr = OptionArgParser::ToAddress(&m_exe_ctx, load_addr_str,
1715435933ddSDimitry Andric LLDB_INVALID_ADDRESS, &error);
1716435933ddSDimitry Andric if (error.Fail() || load_addr == LLDB_INVALID_ADDRESS) {
1717435933ddSDimitry Andric result.AppendErrorWithFormat(
1718435933ddSDimitry Andric "invalid address argument \"%s\": %s\n", command[0].c_str(),
17194bb0738eSEd Maste error.AsCString());
17204bb0738eSEd Maste result.SetStatus(eReturnStatusFailed);
17214bb0738eSEd Maste }
17224bb0738eSEd Maste }
17234bb0738eSEd Maste
17244bb0738eSEd Maste lldb_private::MemoryRegionInfo range_info;
17254bb0738eSEd Maste error = process_sp->GetMemoryRegionInfo(load_addr, range_info);
1726435933ddSDimitry Andric if (error.Success()) {
17274bb0738eSEd Maste lldb_private::Address addr;
1728*b5893f02SDimitry Andric ConstString name = range_info.GetName();
17294bb0738eSEd Maste ConstString section_name;
1730435933ddSDimitry Andric if (process_sp->GetTarget().ResolveLoadAddress(load_addr, addr)) {
17314bb0738eSEd Maste SectionSP section_sp(addr.GetSection());
1732435933ddSDimitry Andric if (section_sp) {
17334bb0738eSEd Maste // Got the top most section, not the deepest section
17344bb0738eSEd Maste while (section_sp->GetParent())
17354bb0738eSEd Maste section_sp = section_sp->GetParent();
17364bb0738eSEd Maste section_name = section_sp->GetName();
17374bb0738eSEd Maste }
17384bb0738eSEd Maste }
17394bb0738eSEd Maste result.AppendMessageWithFormat(
1740*b5893f02SDimitry Andric "[0x%16.16" PRIx64 "-0x%16.16" PRIx64 ") %c%c%c%s%s%s%s\n",
1741435933ddSDimitry Andric range_info.GetRange().GetRangeBase(),
1742435933ddSDimitry Andric range_info.GetRange().GetRangeEnd(),
1743435933ddSDimitry Andric range_info.GetReadable() ? 'r' : '-',
1744435933ddSDimitry Andric range_info.GetWritable() ? 'w' : '-',
1745*b5893f02SDimitry Andric range_info.GetExecutable() ? 'x' : '-',
1746*b5893f02SDimitry Andric name ? " " : "", name.AsCString(""),
1747*b5893f02SDimitry Andric section_name ? " " : "", section_name.AsCString(""));
17484bb0738eSEd Maste m_prev_end_addr = range_info.GetRange().GetRangeEnd();
17494bb0738eSEd Maste result.SetStatus(eReturnStatusSuccessFinishResult);
1750435933ddSDimitry Andric } else {
17514bb0738eSEd Maste result.SetStatus(eReturnStatusFailed);
17524bb0738eSEd Maste result.AppendErrorWithFormat("%s\n", error.AsCString());
17534bb0738eSEd Maste }
17544bb0738eSEd Maste }
1755435933ddSDimitry Andric } else {
17564bb0738eSEd Maste m_prev_end_addr = LLDB_INVALID_ADDRESS;
17574bb0738eSEd Maste result.AppendError("invalid process");
17584bb0738eSEd Maste result.SetStatus(eReturnStatusFailed);
17594bb0738eSEd Maste }
17604bb0738eSEd Maste return result.Succeeded();
17614bb0738eSEd Maste }
17624bb0738eSEd Maste
GetRepeatCommand(Args & current_command_args,uint32_t index)1763435933ddSDimitry Andric const char *GetRepeatCommand(Args ¤t_command_args,
1764435933ddSDimitry Andric uint32_t index) override {
17654bb0738eSEd Maste // If we repeat this command, repeat it without any arguments so we can
17664bb0738eSEd Maste // show the next memory range
17674bb0738eSEd Maste return m_cmd_name.c_str();
17684bb0738eSEd Maste }
17694bb0738eSEd Maste
17704bb0738eSEd Maste lldb::addr_t m_prev_end_addr;
17714bb0738eSEd Maste };
1772ac7ddfbfSEd Maste
1773ac7ddfbfSEd Maste //-------------------------------------------------------------------------
1774ac7ddfbfSEd Maste // CommandObjectMemory
1775ac7ddfbfSEd Maste //-------------------------------------------------------------------------
1776ac7ddfbfSEd Maste
CommandObjectMemory(CommandInterpreter & interpreter)17774bb0738eSEd Maste CommandObjectMemory::CommandObjectMemory(CommandInterpreter &interpreter)
1778435933ddSDimitry Andric : CommandObjectMultiword(
1779435933ddSDimitry Andric interpreter, "memory",
1780435933ddSDimitry Andric "Commands for operating on memory in the current target process.",
1781435933ddSDimitry Andric "memory <subcommand> [<subcommand-options>]") {
1782435933ddSDimitry Andric LoadSubCommand("find",
1783435933ddSDimitry Andric CommandObjectSP(new CommandObjectMemoryFind(interpreter)));
1784435933ddSDimitry Andric LoadSubCommand("read",
1785435933ddSDimitry Andric CommandObjectSP(new CommandObjectMemoryRead(interpreter)));
1786435933ddSDimitry Andric LoadSubCommand("write",
1787435933ddSDimitry Andric CommandObjectSP(new CommandObjectMemoryWrite(interpreter)));
1788435933ddSDimitry Andric LoadSubCommand("history",
1789435933ddSDimitry Andric CommandObjectSP(new CommandObjectMemoryHistory(interpreter)));
1790435933ddSDimitry Andric LoadSubCommand("region",
1791435933ddSDimitry Andric CommandObjectSP(new CommandObjectMemoryRegion(interpreter)));
1792ac7ddfbfSEd Maste }
1793ac7ddfbfSEd Maste
17944bb0738eSEd Maste CommandObjectMemory::~CommandObjectMemory() = default;
1795