blob: 50b8963cdd03481e70dab6686a29a83924f8529d [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_memory.h"
#include <inttypes.h>
#include <limits>
#include "src/developer/debug/zxdb/client/memory_dump.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
const char kUnknownByte[] = "??";
const char kNonAscii = ' '; // When printing ASCII and the character is not in range.
bool IsPrintableAscii(uint8_t c) { return c >= ' ' && c < 0x7f; }
// Formats the given |address|. The |begin_address| is given so we can compute the offset.
std::string GetAddressString(MemoryFormatOptions::AddressMode mode, int addr_width,
uint64_t begin_address, uint64_t address) {
switch (mode) {
case MemoryFormatOptions::kNoAddresses:
return std::string();
case MemoryFormatOptions::kAddresses:
return fxl::StringPrintf("0x%0*" PRIx64 ": ", addr_width, address);
case MemoryFormatOptions::kOffsets:
return fxl::StringPrintf("+0x%0*" PRIx64 ": ", addr_width, address - begin_address);
}
return std::string();
}
} // namespace
// Optimized for simplicity over speed. But this does not use the table output to avoid having giant
// table computations for large memory dumps.
OutputBuffer FormatMemory(const MemoryDump& dump, uint64_t begin, uint32_t size,
const MemoryFormatOptions& opts) {
OutputBuffer out;
// Special-case 0 size because the "max_addr" computation below doesn't make any sense in that
// context.
if (size == 0) {
out.Append(Syntax::kComment, GetAddressString(opts.address_mode, 0, begin, begin));
out.Append("\n");
return out;
}
// Compute the last address we'll print. Watch for overflow.
uint64_t max_addr = std::numeric_limits<uint64_t>::max();
if (max_addr - size > begin)
max_addr = begin + size - 1;
// Max address number character width for the digits to be padded out to (not including "0x"
// prefix).
int addr_width;
switch (opts.address_mode) {
case MemoryFormatOptions::kNoAddresses:
addr_width = 0;
break;
case MemoryFormatOptions::kAddresses:
addr_width = static_cast<int>(fxl::StringPrintf("%" PRIx64, max_addr).size());
break;
case MemoryFormatOptions::kOffsets:
addr_width = static_cast<int>(fxl::StringPrintf("%" PRIx32, size).size());
break;
}
uint64_t cur = begin; // Current address being printed.
std::string line; // These string buffers are outside the loop to prevent reallocation.
OutputBuffer values;
std::string ascii;
bool done = false;
while (!done) { // Line loop
// Compute address at beginning of line.
out.Append(Syntax::kComment,
GetAddressString(opts.address_mode, addr_width, dump.address(), cur));
values.Clear();
ascii = " |";
for (int i = 0; i < opts.values_per_line; i++) { // Value loop.
// Separator between values.
if (i > 0) {
if (!done && opts.separator_every > 0 && (i % opts.separator_every) == 0) {
values.Append(Syntax::kComment, "-");
} else {
values.Append(" ");
}
}
if (!done) {
uint8_t byte;
if (dump.GetByte(cur, &byte)) {
values.Append(fxl::StringPrintf("%02x", static_cast<int>(byte)));
if (IsPrintableAscii(byte)) {
ascii.push_back(static_cast<char>(byte));
} else {
ascii.push_back(kNonAscii);
}
} else {
values.Append(Syntax::kComment, kUnknownByte);
ascii.push_back(kNonAscii);
}
// Carefully only increment address if it won't overflow.
if (cur == max_addr) {
done = true;
} else {
cur++;
}
} else {
// Line is done, but we still want padding for values.
values.Append(" ");
ascii.push_back(' ');
}
}
// Append the constructed elements.
out.Append(std::move(values));
if (opts.show_ascii)
out.Append(Syntax::kComment, std::move(ascii));
out.Append("\n");
}
return out;
}
} // namespace zxdb