blob: 197788bdced392f57286e4018bec0199b7435987 [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/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 long_format) {
Console* console = Console::get();
int active_frame_id = console->context().GetActiveFrameIdForThread(thread);
auto helper = fxl::MakeRefCounted<FormatValue>();
// This doesn't use table output since the format of the stack frames is
// usually so unpredictable.
const auto& frames = thread->GetFrames();
if (frames.empty()) {
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::WithContents(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], helper.get(), FormatValueOptions(), -1);
} else {
OutputBuffer out;
FormatFrame(frames[i], &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 long_format) {
if (thread->HasAllFrames()) {
ListCompletedFrames(thread, long_format);
} else {
thread->SyncFrames([ thread = thread->GetWeakPtr(), long_format ]() {
Console* console = Console::get();
if (thread)
ListCompletedFrames(thread.get(), long_format);
else
console->Output("Thread exited, no frames.\n");
});
}
}
void FormatFrame(const Frame* frame, OutputBuffer* out, int id) {
if (id >= 0)
out->Append(fxl::StringPrintf("Frame %d ", id));
out->Append(DescribeLocation(frame->GetLocation(), false));
}
void FormatFrameLong(const Frame* frame, FormatValue* out,
const FormatValueOptions& options, int id) {
if (id >= 0)
out->Append(OutputBuffer::WithContents(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(DescribeLocation(location, false));
// 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.function()) {
const Function* func = location.function().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->AppendVariableWithName(location.symbol_context(),
frame->GetSymbolDataProvider(), value,
options);
}
}
}
}
} // namespace zxdb