blob: 95d695a3e3a84103cfd0e92ab3af4d88a046e8ee [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 <inttypes.h>
#include <stdlib.h>
#include <map>
#include "garnet/bin/zxdb/client/register.h"
#include "garnet/bin/zxdb/client/session.h"
#include "garnet/bin/zxdb/common/err.h"
#include "garnet/bin/zxdb/common/regex.h"
#include "garnet/bin/zxdb/console/console.h"
#include "garnet/bin/zxdb/console/format_register.h"
#include "garnet/bin/zxdb/console/format_register_arm64.h"
#include "garnet/bin/zxdb/console/format_register_x64.h"
#include "garnet/bin/zxdb/console/format_table.h"
#include "garnet/bin/zxdb/console/output_buffer.h"
#include "garnet/bin/zxdb/console/string_formatters.h"
#include "garnet/public/lib/fxl/logging.h"
#include "garnet/public/lib/fxl/strings/string_printf.h"
namespace zxdb {
using debug_ipc::RegisterCategory;
using debug_ipc::RegisterID;
namespace {
void InternalFormatGeneric(const std::vector<Register>& registers,
OutputBuffer* out) {
std::vector<std::vector<OutputBuffer>> rows;
for (const Register& reg : registers) {
auto color = rows.size() % 2 == 1 ? TextForegroundColor::kDefault
: TextForegroundColor::kLightGray;
rows.push_back(DescribeRegister(reg, color));
}
FormatTable({ColSpec(Align::kRight), ColSpec(Align::kRight), ColSpec()}, rows,
out);
}
Err FormatCategory(const FormatRegisterOptions& options,
RegisterCategory::Type category,
const std::vector<Register>& registers, OutputBuffer* out) {
auto title = fxl::StringPrintf(
"%s Registers\n", debug_ipc::RegisterCategory::TypeToString(category));
out->Append(OutputBuffer(Syntax::kHeading, std::move(title)));
if (registers.empty()) {
out->Append("No registers to show in this category.");
return Err();
}
// We see if architecture specific printing wants to take over.
Err err;
OutputBuffer category_out;
if (options.arch == debug_ipc::Arch::kX64) {
if (FormatCategoryX64(options, category, registers, &category_out, &err)) {
if (err.ok())
out->Append(std::move(category_out));
return err;
}
} else if (options.arch == debug_ipc::Arch::kArm64) {
if (FormatCategoryARM64(options, category, registers, &category_out,
&err)) {
if (err.ok())
out->Append(std::move(category_out));
return err;
}
}
// Generic case.
InternalFormatGeneric(registers, &category_out);
out->Append(std::move(category_out));
return Err();
}
} // namespace
Err FilterRegisters(const FormatRegisterOptions& options,
const RegisterSet& register_set, FilteredRegisterSet* out) {
const auto& category_map = register_set.category_map();
// Used to track how many registers we found when filtering.
int registers_found = 0;
for (const auto& category : options.categories) {
auto it = category_map.find(category);
if (it == category_map.end())
continue;
out->insert({category, {}});
(*out)[category] = {};
auto& registers = (*out)[category];
if (options.filter_regexp.empty()) {
// Add all registers.
registers.reserve(it->second.size());
for (const auto& reg : it->second) {
registers.emplace_back(reg);
registers_found++;
}
} else {
// We use insensitive case regexp matching.
Regex regex;
Err err = regex.Init(options.filter_regexp);
if (err.has_error())
return err;
for (const auto& reg : it->second) {
const char* reg_name = RegisterIDToString(reg.id());
if (regex.Match(reg_name)) {
registers.push_back(reg);
registers_found++;
}
}
}
}
if (registers_found == 0) {
if (options.filter_regexp.empty()) {
return Err("Could not find registers in the selected categories");
} else {
return Err(
"Could not find any registers that match \"%s\" in the selected "
"categories",
options.filter_regexp.data());
}
}
return Err();
}
Err FormatRegisters(const FormatRegisterOptions& options,
const FilteredRegisterSet& filtered_set,
OutputBuffer* out) {
// We should have detected on the filtering stage that we didn't find any
// register.
FXL_DCHECK(!filtered_set.empty());
std::vector<OutputBuffer> out_buffers;
out_buffers.reserve(filtered_set.size());
for (auto kv : filtered_set) {
if (kv.second.empty())
continue;
OutputBuffer cat_out;
Err err = FormatCategory(options, kv.first, kv.second, &cat_out);
if (!err.ok())
return err;
out_buffers.emplace_back(std::move(cat_out));
}
out->Clear();
// Each section is separated by a new line.
for (const auto& buf : out_buffers) {
out->Append(std::move(buf));
out->Append("\n");
}
return Err();
}
// Formatting helpers ----------------------------------------------------------
std::vector<OutputBuffer> DescribeRegister(const Register& reg,
TextForegroundColor color) {
std::vector<OutputBuffer> result;
result.emplace_back(RegisterIDToString(reg.id()), color);
if (reg.size() <= 8) {
// Treat <= 64 bit registers as numbers.
uint64_t value = reg.GetValue();
result.emplace_back(fxl::StringPrintf("0x%" PRIx64, 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.
std::string hex_out;
Err err = GetLittleEndianHexOutput(reg.data(), &hex_out);
if (!err.ok())
result.emplace_back(err.msg(), color);
else
result.emplace_back(std::move(hex_out), color);
}
return result;
}
} // namespace zxdb