blob: 1c0a622aebc3f6c61c1ddea4caddb2d2f7807a9c [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_stack.h"
#include "src/developer/debug/zxdb/client/frame.h"
#include "src/developer/debug/zxdb/client/target.h"
#include "src/developer/debug/zxdb/console/analyze_memory.h"
#include "src/developer/debug/zxdb/console/async_output_buffer.h"
#include "src/developer/debug/zxdb/console/command.h"
#include "src/developer/debug/zxdb/console/command_utils.h"
#include "src/developer/debug/zxdb/console/commands/verb_mem_analyze.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 {
// Needs to not collide with the kMemAnalyze*Switches.
constexpr int kOffsetSwitch = 100;
const char kStackShortHelp[] = "stack / st: Analyze the stack.";
const char kStackHelp[] =
R"(stack [ --offset=<offset> ] [ --num=<lines> ] [ --size=<bytes> ]
[ <address-expression> ]
Alias: "st"
Prints a stack analysis. This is a special case of "mem-analyze" that
defaults to showing the memory address starting at the current frame's stack
pointer, and annotates the values with the current thread's registers and
stack frames.
An explicit address can optionally be provided to begin dumping to dump at
somewhere other than the current frame's stack pointer (this address can be
any expression that evaluates to an address, see "help expressions"), or you
can provide an --offset from the current stack position.
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.
--offset=<offset> | -o <offset>
Offset from the stack pointer to begin dumping. Mutually exclusive with
<address>.
--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
stack
thread 2 stack
stack --num=128 0x43011a14bfc8
)";
Err RunVerbStack(ConsoleContext* context, const Command& cmd) {
Err err = AssertStoppedThreadWithFrameCommand(context, cmd, "stack");
if (err.has_error())
return err;
AnalyzeMemoryOptions opts;
opts.process = cmd.target()->GetProcess();
opts.thread = cmd.thread();
// Begin address.
if (cmd.args().size() == 1) {
// Explicitly provided start address.
Err err = StringToUint64(cmd.args()[0], &opts.begin_address);
if (err.has_error())
return err;
} else if (cmd.args().size() > 1) {
return Err("Too many args to \"stack\", expecting 0 or 1.");
} else {
// Use implicit SP from the frame (with optional --offset).
opts.begin_address = cmd.frame()->GetStackPointer();
if (cmd.HasSwitch(kOffsetSwitch)) {
int offset = 0;
Err err = StringToInt(cmd.GetSwitchValue(kOffsetSwitch), &offset);
if (err.has_error())
return err;
opts.begin_address += offset;
}
}
// Length parameters.
std::optional<uint32_t> input_size;
err = ReadAnalyzeNumAndSizeSwitches(cmd, &input_size);
if (err.has_error())
return err;
if (!input_size)
opts.bytes_to_read = kDefaultAnalyzeByteSize;
else
opts.bytes_to_read = *input_size;
auto async_output = fxl::MakeRefCounted<AsyncOutputBuffer>();
Console::get()->Output(async_output);
AnalyzeMemory(opts, [bytes_to_read = opts.bytes_to_read, async_output](
const Err& err, OutputBuffer output, uint64_t next_addr) {
async_output->Append(std::move(output));
if (err.has_error()) {
async_output->Append(err);
} else {
// Help text for continuation.
async_output->Append(
Syntax::kComment,
fxl::StringPrintf("↓ For more lines: stack -n %d 0x%" PRIx64,
static_cast<int>(bytes_to_read / sizeof(uint64_t)), next_addr));
}
async_output->Complete();
});
return Err();
}
} // namespace
VerbRecord GetStackVerbRecord() {
VerbRecord stack(&RunVerbStack, {"stack", "st"}, kStackShortHelp, kStackHelp,
CommandGroup::kQuery);
stack.switches.emplace_back(kMemAnalyzeSizeSwitch, true, "size", 's');
stack.switches.emplace_back(kMemAnalyzeNumSwitch, true, "num", 'n');
stack.switches.push_back(SwitchRecord(kOffsetSwitch, true, "offset", 'o'));
return stack;
}
} // namespace zxdb