blob: 42f466c6b89e165f80cf36e3c98340b55e84e1eb [file] [log] [blame]
// Copyright 2018 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 "garnet/bin/zxdb/console/format_frame.h"
#include <inttypes.h>
#include "garnet/bin/zxdb/client/frame.h"
#include "garnet/bin/zxdb/client/thread.h"
#include "garnet/bin/zxdb/console/command_utils.h"
#include "garnet/bin/zxdb/console/console.h"
#include "garnet/bin/zxdb/console/format_value.h"
#include "garnet/bin/zxdb/console/format_value_process_context_impl.h"
#include "garnet/bin/zxdb/console/output_buffer.h"
#include "garnet/bin/zxdb/console/string_util.h"
#include "garnet/bin/zxdb/symbols/function.h"
#include "garnet/bin/zxdb/symbols/location.h"
#include "garnet/bin/zxdb/symbols/value.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
void ListCompletedFrames(Thread* thread, bool include_params,
bool long_format) {
Console* console = Console::get();
int active_frame_id = console->context().GetActiveFrameIdForThread(thread);
auto helper = fxl::MakeRefCounted<FormatValue>(
std::make_unique<FormatValueProcessContextImpl>(thread->GetProcess()));
// Formatting used for long format mode.
FormatExprValueOptions format_options;
format_options.verbosity = FormatExprValueOptions::Verbosity::kMinimal;
// This doesn't use table output since the format of the stack frames is
// usually so unpredictable.
const auto& frames = thread->GetStack().GetFrames();
if (frames.empty()) {
if (thread->GetState() != debug_ipc::ThreadRecord::State::kSuspended ||
!(thread->GetState() == debug_ipc::ThreadRecord::State::kBlocked &&
thread->GetBlockedReason() ==
debug_ipc::ThreadRecord::BlockedReason::kException)) {
// Make a nicer error message for the common case of requesting stack
// frames when the thread is in the wrong state.
helper->Append(
"Stack frames are only available when the thread is either suspended "
"or blocked\nin an exception. Use \"pause\" to suspend it.");
} else {
helper->Append("No stack frames.\n");
}
} else {
for (int i = 0; i < static_cast<int>(frames.size()); i++) {
if (i == active_frame_id)
helper->Append(GetRightArrow() + " ");
else
helper->Append(" ");
helper->Append(
OutputBuffer(Syntax::kSpecial, fxl::StringPrintf("%d ", i)));
// Supply "-1" for the frame index to suppress printing (we already
// did it above).
if (long_format) {
FormatFrameLong(frames[i], include_params, helper.get(), format_options,
-1);
} else {
OutputBuffer out;
FormatFrame(frames[i], include_params, &out, -1);
helper->Append(std::move(out));
}
helper->Append("\n");
}
}
helper->Complete(
[helper](OutputBuffer out) { Console::get()->Output(std::move(out)); });
}
} // namespace
void OutputFrameList(Thread* thread, bool include_params, bool long_format) {
// Always request an up-to-date frame list from the agent. Various things
// could have changed and the user is manually requesting a new list, so
// don't rely on the cached copy even if Stack::has_all_frames() is true.
thread->GetStack().SyncFrames(
[ thread = thread->GetWeakPtr(), include_params, long_format ]() {
Console* console = Console::get();
if (thread)
ListCompletedFrames(thread.get(), include_params, long_format);
else
console->Output("Thread exited, no frames.\n");
});
}
void FormatFrame(const Frame* frame, bool include_params, OutputBuffer* out,
int id) {
if (id >= 0)
out->Append(fxl::StringPrintf("Frame %d ", id));
out->Append(FormatLocation(frame->GetLocation(), false, include_params));
}
void FormatFrameLong(const Frame* frame, bool include_params, FormatValue* out,
const FormatExprValueOptions& options, int id) {
if (id >= 0)
out->Append(OutputBuffer(fxl::StringPrintf("Frame %d ", id)));
// Only print the location if it has symbols, otherwise the hex
// address will be shown twice.
const Location& location = frame->GetLocation();
if (location.has_symbols())
out->Append(FormatLocation(location, false, include_params));
// Long format includes the IP address.
// TODO(brettw) handle asynchronously available BP.
uint64_t bp = 0;
if (auto optional_bp = frame->GetBasePointer())
bp = *optional_bp;
out->Append(OutputBuffer(
Syntax::kComment,
fxl::StringPrintf("\n IP = 0x%" PRIx64 ", BP = 0x%" PRIx64
", SP = 0x%" PRIx64,
frame->GetAddress(), bp, frame->GetStackPointer())));
if (location.symbol()) {
const Function* func = location.symbol().Get()->AsFunction();
if (func) {
// Always list function parameters in the order specified.
for (const auto& param : func->parameters()) {
const Variable* value = param.Get()->AsVariable();
if (!value)
continue; // Symbols are corrupt.
out->Append("\n "); // Indent.
out->AppendVariable(location.symbol_context(),
frame->GetSymbolDataProvider(), value, options);
}
}
}
}
} // namespace zxdb