blob: e3456e0d8254a4a8b09c93ad17074908df198642 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/console/commands/verb_mem_analyze.h"
#include "src/developer/debug/zxdb/console/analyze_memory.h"
#include "src/developer/debug/zxdb/console/command.h"
#include "src/developer/debug/zxdb/console/command_utils.h"
#include "src/developer/debug/zxdb/console/console.h"
#include "src/developer/debug/zxdb/console/output_buffer.h"
#include "src/developer/debug/zxdb/console/verbs.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
const char kMemAnalyzeShortHelp[] = "mem-analyze / ma: Analyze a memory region.";
const char kMemAnalyzeHelp[] =
R"(mem-analyze [ --num=<lines> ] [ --size=<size> ] <address-expression>
Alias: "ma"
Prints a memory analysis. A memory analysis attempts to find pointers to
code in pointer-aligned locations and annotates those values.
The address can be an explicit number or any expression ("help expressions")
that evaluates to a memory address.
When no size is given, the size will be the object size if a typed expression
is given, otherwise 20 lines will be output.
See also "stack" which is specialized more for stacks (it includes the
current thread's registers), and "mem-read" to display a simple hex dump.
Arguments
--num=<lines> | -n <lines>
The number of output lines. Each line is the size of one pointer, so
the amount of memory displayed on a 64-bit system will be 8 × num_lines.
Mutually exclusive with --size.
--size=<bytes> | -s <bytes>
The number of bytes to analyze. This will be rounded up to the nearest
pointer boundary. Mutually exclusive with --num.
Examples
ma 0x43011a14bfc8
mem-analyze 0x43011a14bfc8
process 3 mem-analyze 0x43011a14bfc8
mem-analyze --num=128 0x43011a14bfc8
)";
Err RunVerbMemAnalyze(ConsoleContext* context, const Command& cmd) {
// Only a process can have its memory read.
if (Err err = cmd.ValidateNouns({Noun::kProcess}); err.has_error())
return err;
// Length parameters.
std::optional<uint32_t> input_size;
if (Err err = ReadAnalyzeNumAndSizeSwitches(cmd, &input_size); err.has_error())
return err;
return EvalCommandAddressExpression(
cmd, "mem-analyze", GetEvalContextForCommand(cmd),
[weak_target = cmd.target()->GetWeakPtr(), input_size](const Err& err, uint64_t address,
std::optional<uint64_t> object_size) {
Console* console = Console::get();
if (err.has_error()) {
console->Output(err); // Evaluation error.
return;
}
if (!weak_target) {
// Target has been destroyed during evaluation. Normally a message will be printed when
// that happens so we can skip reporting the error.
return;
}
Err run_err = AssertRunningTarget(&console->context(), "mem-read", weak_target.get());
if (run_err.has_error()) {
console->Output(run_err);
return;
}
AnalyzeMemoryOptions opts;
opts.process = weak_target->GetProcess();
opts.begin_address = address;
if (input_size)
opts.bytes_to_read = *input_size;
else if (object_size)
opts.bytes_to_read = *object_size;
else
opts.bytes_to_read = kDefaultAnalyzeByteSize;
AnalyzeMemory(opts, [bytes_to_read = opts.bytes_to_read](
const Err& err, OutputBuffer output, uint64_t next_addr) {
if (err.has_error()) {
output.Append(err);
} else {
// Help text for continuation.
output.Append(
Syntax::kComment,
fxl::StringPrintf("↓ For more lines: ma -n %d 0x%" PRIx64,
static_cast<int>(bytes_to_read / sizeof(uint64_t)), next_addr));
}
Console::get()->Output(output);
});
});
}
} // namespace
const int kMemAnalyzeSizeSwitch = 1;
const int kMemAnalyzeNumSwitch = 2;
// Gives 20 lines of output which fits on a terminal without scrolling (plus one line of help text,
// the next prompt, and the command itself).
const uint32_t kDefaultAnalyzeByteSize = 160;
VerbRecord GetMemAnalyzeVerbRecord() {
VerbRecord mem_analyze(&RunVerbMemAnalyze, {"mem-analyze", "ma"}, kMemAnalyzeShortHelp,
kMemAnalyzeHelp, CommandGroup::kQuery);
mem_analyze.switches.emplace_back(kMemAnalyzeSizeSwitch, true, "size", 's');
mem_analyze.switches.emplace_back(kMemAnalyzeNumSwitch, true, "num", 'n');
mem_analyze.param_type = VerbRecord::kOneParam;
return mem_analyze;
}
Err ReadAnalyzeNumAndSizeSwitches(const Command& cmd, std::optional<uint32_t>* out_size) {
if (cmd.HasSwitch(kMemAnalyzeNumSwitch) && cmd.HasSwitch(kMemAnalyzeSizeSwitch))
return Err("Can't specify both --num and --size.");
if (cmd.HasSwitch(kMemAnalyzeSizeSwitch)) {
// Size argument.
uint32_t parsed;
Err err = StringToUint32(cmd.GetSwitchValue(kMemAnalyzeSizeSwitch), &parsed);
if (err.has_error())
return err;
*out_size = parsed;
} else if (cmd.HasSwitch(kMemAnalyzeNumSwitch)) {
// Num lines argument.
uint32_t num_lines;
Err err = StringToUint32(cmd.GetSwitchValue(kMemAnalyzeNumSwitch), &num_lines);
if (err.has_error())
return err;
*out_size = num_lines * sizeof(uint64_t); // Convert pointer count to size.
}
return Err();
}
} // namespace zxdb