| // 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 |