blob: 12aac4f249b69f85415656a6b594c1fff02ac491 [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_register.h"
#include <lib/syslog/cpp/macros.h>
#include <stdlib.h>
#include <algorithm>
#include <map>
#include "src/developer/debug/shared/regex.h"
#include "src/developer/debug/shared/register_id.h"
#include "src/developer/debug/shared/register_info.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/common/string_util.h"
#include "src/developer/debug/zxdb/console/console.h"
#include "src/developer/debug/zxdb/console/format_register_arm64.h"
#include "src/developer/debug/zxdb/console/format_register_x64.h"
#include "src/developer/debug/zxdb/console/format_table.h"
#include "src/developer/debug/zxdb/console/output_buffer.h"
#include "src/developer/debug/zxdb/console/string_formatters.h"
#include "src/developer/debug/zxdb/expr/abi_null.h"
#include "src/developer/debug/zxdb/expr/eval_context_impl.h"
#include "src/developer/debug/zxdb/expr/format.h"
#include "src/developer/debug/zxdb/expr/format_options.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
void FormatCategory(const FormatRegisterOptions& options, debug::RegisterCategory category,
const std::vector<debug::RegisterValue>& registers, OutputBuffer* out) {
auto title = fxl::StringPrintf("%s Registers\n", debug::RegisterCategoryToString(category));
out->Append(OutputBuffer(Syntax::kHeading, std::move(title)));
if (registers.empty()) {
out->Append("No registers to show in this category.");
return;
}
// Check for architecture-specific printing.
if (options.arch == debug::Arch::kX64) {
if (FormatCategoryX64(options, category, registers, out))
return;
} else if (options.arch == debug::Arch::kArm64) {
if (FormatCategoryARM64(options, category, registers, out))
return;
}
// General formatting.
FormatGeneralRegisters(registers, out);
}
} // namespace
OutputBuffer FormatRegisters(const FormatRegisterOptions& options,
const std::vector<debug::RegisterValue>& registers) {
OutputBuffer out;
// Group register by category.
std::map<debug::RegisterCategory, std::vector<debug::RegisterValue>> categorized;
for (const debug::RegisterValue& reg : registers)
categorized[debug::RegisterIDToCategory(reg.id)].push_back(reg);
for (auto& [category, cat_regs] : categorized) {
// Ensure the registers appear in a consistent order.
std::sort(cat_regs.begin(), cat_regs.end(),
[](const debug::RegisterValue& a, const debug::RegisterValue& b) {
return static_cast<uint32_t>(a.id) < static_cast<uint32_t>(b.id);
});
FormatCategory(options, category, cat_regs, &out);
out.Append("\n");
}
return out;
}
void FormatGeneralRegisters(const std::vector<debug::RegisterValue>& registers, OutputBuffer* out) {
std::vector<std::vector<OutputBuffer>> rows;
for (const debug::RegisterValue& reg : registers) {
auto color =
rows.size() % 2 == 1 ? TextForegroundColor::kDefault : TextForegroundColor::kLightGray;
rows.push_back(DescribeRegister(reg, color));
}
// Pad left by two spaces so the headings make more sense.
FormatTable({ColSpec(Align::kRight, 0, std::string(), 2), ColSpec(Align::kRight), ColSpec()},
rows, out);
}
void FormatGeneralVectorRegisters(const FormatRegisterOptions& options,
const std::vector<debug::RegisterValue>& registers,
OutputBuffer* out) {
bool is_float = options.vector_format == VectorRegisterFormat::kFloat ||
options.vector_format == VectorRegisterFormat::kDouble;
FormatOptions format_options;
if (!is_float) {
// Force padded hex output for all non-floating-point values.
format_options.num_format = FormatOptions::NumFormat::kHex;
format_options.zero_pad_hex_bin = true;
}
// The formatter needs an eval context but we don't need it to have any capabilities.
auto eval_context = fxl::MakeRefCounted<EvalContextImpl>(
std::make_shared<AbiNull>(), fxl::WeakPtr<const ProcessSymbols>(),
fxl::RefPtr<SymbolDataProvider>(), Location());
// Largest number of vector elements of all registers.
size_t max_children = 0;
// Convert each register to a FormatNode. It will have each vector element as the children.
std::vector<std::unique_ptr<FormatNode>> formatted;
for (const auto& r : registers) {
// Use the expression formatter to format the vector members.
ExprValue vector_value = VectorRegisterToValue(r.id, options.vector_format, r.data);
auto node =
std::make_unique<FormatNode>(debug::RegisterIDToString(r.id), std::move(vector_value));
// In general formatting is asynchronous but a vector of numbers should always be completable
// synchronously.
bool completed = false;
FillFormatNodeDescription(node.get(), format_options, eval_context,
fit::defer_callback([&completed]() { completed = true; }));
FX_DCHECK(completed);
max_children = std::max(max_children, node->children().size());
formatted.push_back(std::move(node));
}
// Convert the formatted registers to a table.
std::vector<std::vector<OutputBuffer>> rows;
for (auto& node : formatted) {
// Each row is the name + the children elements.
std::vector<OutputBuffer>& row = rows.emplace_back();
row.resize(max_children + 1);
auto color =
rows.size() % 2 == 1 ? TextForegroundColor::kLightGray : TextForegroundColor::kDefault;
row[0] = OutputBuffer(node->name(), color); // debug::RegisterValue name.
// Add each child to the row.
for (size_t i = 0; i < node->children().size(); i++) {
bool completed = false;
FillFormatNodeDescription(node->children()[i].get(), format_options, eval_context,
fit::defer_callback([&completed]() { completed = true; }));
FX_DCHECK(completed);
// The table is filled with the low index on the right.
row[row.size() - i - 1] = OutputBuffer(node->children()[i]->description(), color);
}
}
std::vector<ColSpec> spec;
spec.emplace_back(Align::kRight, 0, "Name", 2);
for (size_t i = 0; i < max_children; i++)
spec.emplace_back(Align::kRight, 0, fxl::StringPrintf("[%zu]", max_children - i - 1));
FormatTable(spec, rows, out);
out->Append(Syntax::kComment,
fxl::StringPrintf(
" (Use \"get/set vector-format\" to control vector register intepretation.\n"
" Currently showing vectors of \"%s\".)\n",
VectorRegisterFormatToString(options.vector_format)));
}
std::vector<OutputBuffer> DescribeRegister(const debug::RegisterValue& reg,
TextForegroundColor color) {
std::vector<OutputBuffer> result;
result.emplace_back(debug::RegisterIDToString(reg.id), color);
if (reg.data.size() <= 8) {
// Treat <= 64 bit registers as numbers.
uint64_t value = static_cast<uint64_t>(reg.GetValue());
result.emplace_back(to_hex_string(value), color);
// For plausible small integers, show the decimal value also. This size check is intended to
// avoid cluttering up the results with large numbers corresponding to pointers.
constexpr uint64_t kMaxSmallMagnitude = 0xffff;
if (value <= kMaxSmallMagnitude || llabs(static_cast<long long int>(value)) <=
static_cast<long long int>(kMaxSmallMagnitude)) {
result.emplace_back(fxl::StringPrintf("= %d", static_cast<int>(value)), color);
} else {
result.emplace_back();
}
} else {
// Assume anything bigger than 64 bits is a vector and print with grouping.
result.emplace_back(GetLittleEndianHexOutput(reg.data));
}
return result;
}
} // namespace zxdb