blob: 68dee52e64971b73f157349f4e154f91515da8f7 [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 "src/developer/debug/zxdb/console/format_frame.h"
#include <inttypes.h>
#include <lib/syslog/cpp/macros.h>
#include "src/developer/debug/zxdb/client/frame.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/console/command_utils.h"
#include "src/developer/debug/zxdb/console/console.h"
#include "src/developer/debug/zxdb/console/format_location.h"
#include "src/developer/debug/zxdb/console/format_node_console.h"
#include "src/developer/debug/zxdb/console/output_buffer.h"
#include "src/developer/debug/zxdb/console/string_util.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/location.h"
#include "src/developer/debug/zxdb/symbols/value.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
// Appends the frame heading (indent, active marker, frame number) for a stack entry. The frame
// numbers are expressed in a range (inclusive) to support pretty-printing. They should be the same
// for normal stack entries.
void AppendFrameNumber(size_t begin_range, size_t end_range, size_t active_frame_index,
AsyncOutputBuffer* out) {
if (active_frame_index >= begin_range && active_frame_index <= end_range)
out->Append(GetCurrentRowMarker() + " ");
else
out->Append(" ");
if (begin_range == end_range) {
out->Append(OutputBuffer(Syntax::kSpecial, fxl::StringPrintf("%zu ", begin_range)));
} else {
out->Append(OutputBuffer(Syntax::kSpecial, fxl::StringPrintf("%zu", begin_range)));
out->Append(Syntax::kComment, "…");
out->Append(OutputBuffer(Syntax::kSpecial, fxl::StringPrintf("%zu ", end_range)));
}
}
fxl::RefPtr<AsyncOutputBuffer> ListCompletedFrames(Thread* thread, const FormatStackOptions& opts) {
size_t active_frame_id =
static_cast<size_t>(Console::get()->context().GetActiveFrameIdForThread(thread));
auto out = fxl::MakeRefCounted<AsyncOutputBuffer>();
// This doesn't use table output since the format of the stack frames is usually so unpredictable.
const Stack& stack = thread->GetStack();
if (stack.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.
out->Append(
"Stack frames are only available when the thread is either suspended "
"or blocked\nin an exception. Use \"pause\" to suspend it.");
} else {
out->Append("No stack frames.\n");
}
out->Complete();
return out;
}
std::vector<PrettyStackManager::FrameEntry> pretty_stack;
if (opts.pretty_stack) {
pretty_stack = opts.pretty_stack->ProcessStack(stack);
} else {
pretty_stack.resize(stack.size());
for (size_t i = 0; i < stack.size(); i++) {
pretty_stack[i].begin_index = i;
pretty_stack[i].frames.push_back(stack[i]);
}
}
for (const auto& entry : pretty_stack) {
// Stack item pretty-printing only happens if there's a pretty match and the current entry
// isn't within the range of midden frames.
//
// One case this doesn't handle is if expanding the range of pretty-stacks means a smaller
// matcher might apply that doesn't overlap the user's current frame. To support that, we'd
// need to move the logic of no prettifying the current frame to the PrettyStackManager,
bool use_pretty = entry.match && !(active_frame_id > entry.begin_index &&
active_frame_id < entry.begin_index + entry.frames.size());
if (use_pretty) {
AppendFrameNumber(entry.begin_index, entry.begin_index + entry.frames.size() - 1,
active_frame_id, out.get());
out->Append("«" + entry.match.description + "»");
out->Append(Syntax::kComment, " (-r expands)\n");
} else {
for (size_t i = 0; i < entry.frames.size(); i++) {
AppendFrameNumber(entry.begin_index + i, entry.begin_index + i, active_frame_id, out.get());
// Supply "-1" for the frame index to suppress printing (we already did it).
out->Append(FormatFrame(stack[entry.begin_index + i], opts.frame, -1));
out->Append("\n");
}
}
}
out->Complete();
return out;
}
} // namespace
fxl::RefPtr<AsyncOutputBuffer> FormatStack(Thread* thread, bool force_update,
const FormatStackOptions& opts) {
auto out = fxl::MakeRefCounted<AsyncOutputBuffer>();
if (!force_update && thread->GetStack().has_all_frames()) {
out->Complete(ListCompletedFrames(thread, opts));
return out;
}
// Request a stack update.
thread->GetStack().SyncFrames([thread = thread->GetWeakPtr(), opts, out](const Err& err) {
if (!err.has_error() && thread)
out->Complete(ListCompletedFrames(thread.get(), opts));
else
out->Complete("Thread exited, no frames.\n");
});
return out;
}
fxl::RefPtr<AsyncOutputBuffer> FormatFrame(const Frame* frame, const FormatFrameOptions& opts,
int id) {
auto out = fxl::MakeRefCounted<AsyncOutputBuffer>();
if (id >= 0) {
out->Append("Frame ");
out->Append(Syntax::kSpecial, std::to_string(id));
out->Append(" ");
}
const Location& location = frame->GetLocation();
out->Append(FormatLocation(location, opts.loc));
if (frame->IsInline())
out->Append(Syntax::kComment, " (inline)");
// IP address and stack pointers.
if (opts.detail == FormatFrameOptions::kVerbose) {
out->Append(Syntax::kComment, fxl::StringPrintf("\n IP = 0x%" PRIx64 ", SP = 0x%" PRIx64,
frame->GetAddress(), frame->GetStackPointer()));
// TODO(brettw) make this work when the frame base is asynchronous.
if (auto bp = frame->GetBasePointer())
out->Append(Syntax::kComment, fxl::StringPrintf(", base = 0x%" PRIx64, *bp));
}
if (opts.detail != FormatFrameOptions::kSimple && location.symbol()) {
const Function* func = location.symbol().Get()->As<Function>();
if (func) {
// Always list function parameters in the order specified.
for (const auto& param : func->parameters()) {
const Variable* value = param.Get()->As<Variable>();
if (!value)
continue; // Symbols are corrupt.
out->Append("\n "); // Indent.
out->Append(FormatVariableForConsole(value, opts.variable, frame->GetEvalContext()));
}
}
}
out->Complete();
return out;
}
} // namespace zxdb