Hermes
memory_dump.h
Go to the documentation of this file.
1 
32 #ifndef HERMES_LOG_MEMORY_DUMP_H
33 #define HERMES_LOG_MEMORY_DUMP_H
34 
35 #include <hermes/common/debug.h>
36 #include <hermes/common/defs.h>
37 #include <hermes/common/str.h>
38 #include <hermes/numeric/numeric.h>
41 #include <iostream>
42 #include <iomanip>
43 #include <cstdlib> // system
44 
45 namespace hermes {
46 
50  none = 0x0,
51  binary = 0x1,
52  decimal = 0x2,
53  hexadecimal = 0x4,
54  hexii = 0x8,
55  hide_header = 0x10,
56  cache_align = 0x20,
57  hide_zeros = 0x40,
58  show_ascii = 0x80,
59  save_to_string = 0x100,
60  write_to_console = 0x200,
61  colored_output = 0x400,
62  type_values = 0x800
63 };
66 
67 // *********************************************************************************************************************
68 // MemoryDumper
69 // *********************************************************************************************************************
71 class MemoryDumper {
72 public:
97  struct RegionLayout {
99  RegionLayout() = default;
103  RegionLayout &withOffset(std::size_t offset_in_bytes) {
104  offset = offset_in_bytes;
105  return *this;
106  }
110  RegionLayout &withColor(const std::string &console_color) {
111  color = console_color;
112  return *this;
113  }
117  RegionLayout &withCount(std::size_t region_count) {
118  count = region_count;
119  return *this;
120  }
125  type = t;
126  return *this;
127  }
132  RegionLayout &withSubRegion(const RegionLayout &sub_region, bool increment_to_parent_size = false) {
133  std::size_t new_offset = 0;
134  if (!sub_regions.empty())
135  new_offset = sub_regions.back().offset + sub_regions.back().field_size_in_bytes * sub_regions.back().count;
136  sub_regions.push_back(sub_region);
137  sub_regions.back().offset = new_offset;
138  if (increment_to_parent_size)
139  field_size_in_bytes += sub_region.field_size_in_bytes * sub_region.count;
140  return *this;
141  }
145  void pushSubRegion(const RegionLayout &sub_region, bool increment_to_parent_size = false) {
146  std::size_t new_offset = 0;
147  if (!sub_regions.empty())
148  new_offset = sub_regions.back().offset + sub_regions.back().field_size_in_bytes * sub_regions.back().count;
149  sub_regions.push_back(sub_region);
150  sub_regions.back().offset = new_offset;
151  if (increment_to_parent_size)
152  field_size_in_bytes += sub_region.field_size_in_bytes * sub_region.count;
153  }
157  template<typename T>
159  type = DataTypes::typeFrom<T>();
160  return *this;
161  }
166  template<typename T>
167  RegionLayout &withSizeOf(std::size_t element_count = 1) {
168  count = element_count;
169  field_size_in_bytes = sizeof(T);
170  return *this;
171  }
176  RegionLayout &withSize(std::size_t size_in_bytes, std::size_t element_count = 1) {
177  count = element_count;
178  field_size_in_bytes = size_in_bytes;
179  return *this;
180  }
183  [[nodiscard]] std::size_t sizeInBytes() const { return field_size_in_bytes * count; }
186  void resizeSubRegions(size_t sub_regions_count) {
187  sub_regions.resize(sub_regions_count);
189  for (auto &s : sub_regions)
190  field_size_in_bytes += s.field_size_in_bytes * s.count;
191  }
193  void clear() {
194  *this = RegionLayout();
195  }
196 
197  std::size_t offset{0};
198  std::size_t field_size_in_bytes{0};
199  std::size_t count{1};
201  std::vector<RegionLayout> sub_regions{};
202  DataType type{DataType::CUSTOM};
203  };
204 
205  // *******************************************************************************************************************
206  // STATIC METHODS
207  // *******************************************************************************************************************
213  template<typename T>
214  static std::string dumpInfo(const T *data, std::size_t size) {
215  auto alignment = alignof(T);
216  auto ptr = reinterpret_cast<const u8 * >(data);
217  ptrdiff_t down_shift = reinterpret_cast<uintptr_t >(ptr) & (64 - 1);
218  uintptr_t aligned_base_address = reinterpret_cast<uintptr_t >(ptr) - down_shift;
219  auto size_in_bytes = sizeof(T) * size + down_shift;
220  Str s = "Memory Block Information\n";
221  s.appendLine(" Address:\t", Str::addressOf(reinterpret_cast<uintptr_t>(data)));
222  s.appendLine(" Block Size:\t", sizeof(T) * size, " bytes");
223  s.appendLine(" Left Alignment");
224  s.appendLine(" Type Alignment:\t", alignment);
225  s.appendLine(" Shift:\t", down_shift);
226  s.appendLine(" Address:\t", Str::addressOf(reinterpret_cast<uintptr_t>(aligned_base_address)));
227  s.appendLine(" Total Block Size:\t", size_in_bytes, " bytes");
228  return s.str();
229  }
238  template<typename T>
239  static std::string dump(const T *data, std::size_t size, u32 bytes_per_row = 8,
240  const RegionLayout &region = RegionLayout(),
241  memory_dumper_options options = memory_dumper_options::none) {
242  // check options_
243  auto hide_zeros = HERMES_MASK_BIT(options, memory_dumper_options::hide_zeros);
244  auto include_header = !HERMES_MASK_BIT(options, memory_dumper_options::hide_header);
245  auto align_data = HERMES_MASK_BIT(options, memory_dumper_options::cache_align);
246  auto show_ascii = HERMES_MASK_BIT(options, memory_dumper_options::show_ascii);
247  auto write_to_console = HERMES_MASK_BIT(options, memory_dumper_options::write_to_console);
248  auto save_string = HERMES_MASK_BIT(options, memory_dumper_options::save_to_string);
249  auto colored_output = HERMES_MASK_BIT(options, memory_dumper_options::colored_output);
250  auto show_type_values = HERMES_MASK_BIT(options, memory_dumper_options::type_values);
251  if (!write_to_console && !save_string)
252  write_to_console = true;
253  // output string
254  Str output_string;
255  // address size
256  u32 address_digit_count = 8;
257  // compute column size for text alignment
258  u8 data_digit_count = 2;
259  if (HERMES_MASK_BIT(options, memory_dumper_options::decimal))
260  data_digit_count = 3;
261  else if (HERMES_MASK_BIT(options, memory_dumper_options::binary))
262  data_digit_count = 8;
263  u8 header_digit_count = Numbers::countHexDigits(bytes_per_row);
264  u8 column_size = std::max(header_digit_count, data_digit_count);
265  u8 address_column_size = address_digit_count + 2 + 2; // 0x + \t
266  if (include_header) {
267  Str s = std::string(address_column_size, ' ');
268  for (u32 i = 0; i < bytes_per_row; ++i) {
269  auto bs = Str::binaryToHex(i, true, true);
270  if (i % 8 == 0)
271  s.append(" ");
272  s.append(std::setw(column_size), !bs.empty() ? bs : "0", " ");
273  }
274  if (save_string)
275  output_string += s;
276  if (write_to_console)
277  std::cout << s;
278  }
279  auto alignment = (align_data) ? 64 : 1;
280  auto ptr = reinterpret_cast<const u8 * >(data);
281  ptrdiff_t shift = reinterpret_cast<uintptr_t >(ptr) & (alignment - 1);
282  uintptr_t aligned_base_address = reinterpret_cast<uintptr_t >(ptr) - shift;
283  ptrdiff_t byte_offset = 0;
284  ptrdiff_t size_in_bytes = sizeof(T) * size + shift;
285  auto line_count = 0;
286  while (byte_offset < size_in_bytes) {
287  { // ADDRESS
288  Str s;
289  s.appendLine();
290  s.append(Str::addressOf(reinterpret_cast<uintptr_t >((void *) (aligned_base_address + byte_offset))).c_str(),
291  " ");
292  if (save_string)
293  output_string += s;
294  if (write_to_console) {
295  if (colored_output && line_count % 2)
296  std::cout << ConsoleColors::dim << s << ConsoleColors::reset_dim;
297  else
298  std::cout << s;
299  }
300  line_count++;
301  }
302  std::string ascii_data;
303  std::string type_values;
304  for (ptrdiff_t i = 0; i < bytes_per_row; i++, byte_offset++) {
305  if (i % 8 == 0) {
306  if (write_to_console)
307  std::cout << " ";
308  if (save_string)
309  output_string.append(" ");
310  }
311  if (aligned_base_address + byte_offset < reinterpret_cast<uintptr_t >(ptr) || byte_offset >= size_in_bytes) {
312  if (write_to_console)
313  std::cout << std::string(column_size, ' ') + " ";
314  if (save_string)
315  output_string += std::string(column_size, ' ') + " ";
316  ascii_data += '.';
317  continue;
318  }
319  u8 byte = *(reinterpret_cast<u8 *>(aligned_base_address + byte_offset));
320  Str s;
321  if (!hide_zeros || byte) {
322  if (HERMES_MASK_BIT(options, memory_dumper_options::hexadecimal))
323  s.append(Str::binaryToHex(byte), " ");
324  else if (HERMES_MASK_BIT(options, memory_dumper_options::decimal))
325  s.append(std::setfill('0'), std::setw(column_size), static_cast<u32>(byte), ' ');
326  else if (HERMES_MASK_BIT(options, memory_dumper_options::binary))
327  s.append(Str::byteToBinary(byte), " ");
328  else if (HERMES_MASK_BIT(options, memory_dumper_options::hexii))
329  s.append(std::string(column_size, ' '), " ");
330  else
331  s.append(Str::binaryToHex(byte), " ");
332  } else
333  s.append(std::string(column_size, ' '), " ");
334 
335  if (save_string)
336  output_string += s;
337  std::string current_byte_color = byteColor(byte_offset - shift, region);
338  if (write_to_console) {
339  if (colored_output)
340  std::cout << current_byte_color << s.str() << ConsoleColors::default_color << ConsoleColors::reset;
341  else
342  std::cout << s.str();
343  }
344  if (colored_output)
345  ascii_data += current_byte_color;
346  if (std::isalnum(byte))
347  ascii_data += byte;
348  else
349  ascii_data += '.';
350  if (colored_output) {
351  ascii_data += ConsoleColors::default_color;
352  ascii_data += ConsoleColors::reset;
353  }
354  // compute type value (if any)
355  if (show_type_values) {
356  if (colored_output)
357  type_values += current_byte_color;
358  type_values +=
359  typeValue(byte_offset - shift, reinterpret_cast<u8 *>(aligned_base_address + byte_offset), region);
360  if (colored_output) {
363  }
364  type_values += " ";
365  }
366  }
367  if (show_ascii) {
368  if (write_to_console)
369  std::cout << "\t|" << ascii_data << "|";
370  if (save_string)
371  output_string.append("\t|", ascii_data, "|");
372  }
373  if (show_type_values) {
374  if (write_to_console)
375  std::cout << "\t<" << type_values << ">";
376  if (save_string)
377  output_string.append("\t<", type_values, ">");
378  }
379  }
380  if (save_string)
381  output_string += '\n';
382  if (write_to_console)
383  std::cout << "\n";
384  return output_string.str();
385  }
386 
387 private:
388  static std::string byteColor(std::size_t byte_index, const RegionLayout &region) {
389  std::function<std::string(const std::vector<RegionLayout> &, std::size_t, const std::string &)> f;
390  f = [&](const std::vector<RegionLayout> &subregions, std::size_t byte_offset,
391  const std::string &parent_color) -> std::string {
392  for (const auto &sub_region : subregions) {
393  auto region_start = sub_region.offset;
394  auto region_end = region_start + sub_region.field_size_in_bytes * sub_region.count;
395  if (byte_offset >= region_start && byte_offset < region_end) {
396  if (sub_region.sub_regions.empty()) {
397  // in the case of an array of elements, lets alternate between dimmed
398  // colors to make it easy to visually identify elements
399  if (((byte_offset - region_start) / sub_region.field_size_in_bytes) % 2)
400  return ConsoleColors::combine(ConsoleColors::bold, sub_region.color);
401  return sub_region.color;
402  }
403  return f(sub_region.sub_regions,
404  (byte_offset - region_start) % sub_region.field_size_in_bytes,
405  sub_region.color);
406  }
407  }
408  return parent_color;
409  };
410  return f({region}, byte_index, ConsoleColors::default_color);
411  }
412 
413  static std::string typeValue(std::size_t byte_index, u8 *data, const RegionLayout &region) {
414  std::function<std::string(const std::vector<RegionLayout> &, std::size_t, const std::string &)> f;
415  f = [&](const std::vector<RegionLayout> &subregions, std::size_t byte_offset,
416  const std::string &parent_color) -> std::string {
417  HERMES_UNUSED_VARIABLE(parent_color);
418  for (const auto &sub_region : subregions) {
419  auto region_start = sub_region.offset;
420  auto region_end = region_start + sub_region.field_size_in_bytes * sub_region.count;
421  if (byte_offset >= region_start && byte_offset < region_end) {
422  if (sub_region.sub_regions.empty() && sub_region.type != DataType::CUSTOM) {
423  if ((byte_offset - region_start) % DataTypes::typeSize(sub_region.type) == 0) {
424  std::stringstream ss;
425 #define RETURN_TYPE(T) if(sub_region.type == DataTypes::typeFrom<T>()) { \
426  ss << std::setw(10) << std::right << std::setprecision(3) << *reinterpret_cast<T*>(data); \
427  return ss.str(); }
428  RETURN_TYPE(i8)
429  RETURN_TYPE(i16)
430  RETURN_TYPE(i32)
431  RETURN_TYPE(i64)
432  RETURN_TYPE(u8)
433  RETURN_TYPE(u16)
434  RETURN_TYPE(u32)
435  RETURN_TYPE(u64)
436  RETURN_TYPE(f32)
437  RETURN_TYPE(f64)
438 #undef RETURN_TYPE
439  return "ERROR";
440  }
441  return "";
442  }
443  return f(sub_region.sub_regions,
444  (byte_offset - region_start) % sub_region.field_size_in_bytes,
445  sub_region.color);
446  }
447  }
448  return "";
449  };
450  return f({region}, byte_index, ConsoleColors::default_color);
451  }
452 
453 };
454 
459 inline std::ostream &operator<<(std::ostream &os, const MemoryDumper::RegionLayout &layout) {
460  os << layout.color << "MemoryRegionLayout [offset = " << layout.offset;
461  os << " field size (bytes) = " << layout.field_size_in_bytes;
462  os << " count = " << layout.count;
463  os << " type = " << DataTypes::typeName(layout.type) << "]\n";
464  os << "\tsub regions [" << layout.sub_regions.size() << "]\n";
465  if (!layout.sub_regions.empty())
466  for (const auto &s : layout.sub_regions) {
467  os << s;
468  os << "\n";
469  }
470  os << "\n";
471  return os;
472 }
473 
474 }
475 
476 #endif //HERMES_HERMES_HERMES_LOG_MEMORY_DUMP_H
477 
Support of bitwise operations for compatible enum classes.
#define HERMES_MASK_BIT(MASK, BIT)
Tests if enum class value is enabled.
Definition: bitmask_operators.h:84
#define HERMES_ENABLE_BITMASK_OPERATORS(x)
Adds bitwise operation support to a given enum class.
Definition: bitmask_operators.h:58
static std::string combine(const std::string &a, const std::string &b)
Combine two color codes.
Definition: console_colors.h:112
static char bold[5]
"\e[1m"
Definition: console_colors.h:46
static char reset[5]
"\e[0m"
Definition: console_colors.h:53
static char dim[5]
"\e[2m"
Definition: console_colors.h:47
static char reset_dim[6]
"\e[22m"
Definition: console_colors.h:55
static char default_color[6]
"\e[39m"
Definition: console_colors.h:61
static std::string typeName(DataType type)
Gets DataType string name.
Definition: defs.h:183
static u32 typeSize(DataType type)
Computes number of bytes from DataType.
Definition: defs.h:163
Auxiliary logging class for printing blocks of memory.
Definition: memory_dump.h:71
static std::string dump(const T *data, std::size_t size, u32 bytes_per_row=8, const RegionLayout &region=RegionLayout(), memory_dumper_options options=memory_dumper_options::none)
Dumps memory region.
Definition: memory_dump.h:239
static std::string dumpInfo(const T *data, std::size_t size)
Dumps memory info about a given memory region.
Definition: memory_dump.h:214
String class and set of string functions.
Definition: str.h:53
static std::string binaryToHex(T input_n, bool uppercase=true, bool strip_leading_zeros=false)
Get ascii representation of raw bit data of input_n
Definition: str.h:267
void append(const Args &... args)
Append arguments to this Str.
Definition: str.h:404
static std::string addressOf(uintptr_t ptr, u32 digit_count=8)
Generates hexadecimal representation of memory address.
Definition: str.h:291
const std::string & str() const
Get std::string object.
Definition: str.h:381
void appendLine(const Args &... args)
Append arguments to this Str followed by a breakline.
Definition: str.h:414
static std::string byteToBinary(byte b)
Binary representation of byte.
Definition: str.h:303
Set of 256-terminal supported color codes.
Debug, logging and assertion macros.
Data type definitions.
DataType
Enum class for integral types.
Definition: defs.h:101
@ none
default behaviour
int8_t i8
8 bit size integer type
Definition: defs.h:81
uint64_t u64
64 bit size unsigned integer type
Definition: defs.h:89
#define HERMES_UNUSED_VARIABLE(x)
Specifies that variable is not used in this scope.
Definition: debug.h:62
uint8_t byte
unsigned byte
Definition: defs.h:96
uint16_t u16
16 bit size unsigned integer type
Definition: defs.h:87
uint32_t u32
32 bit size unsigned integer type
Definition: defs.h:88
int64_t i64
64 bit size integer type
Definition: defs.h:84
int16_t i16
16 bit size integer type
Definition: defs.h:82
uint8_t u8
8 bit size unsigned integer type
Definition: defs.h:86
double f64
64 bit size floating point type
Definition: defs.h:79
float f32
32 bit size floating point type
Definition: defs.h:78
int32_t i32
32 bit size integer type
Definition: defs.h:83
memory_dumper_options
MemoryDumper output options.
Definition: memory_dump.h:49
@ hexadecimal
output memory contents in hexadecimal
@ hexii
output memory contents in hexii
@ cache_align
aligns starting address to cache size
@ write_to_console
directly dump into stdout
@ binary
output memory contents in binary
@ decimal
output memory contents in decimal
@ hide_zeros
do not output bytes with 0 as value
@ save_to_string
redirect output to string
@ type_values
cast values and output their values properly
@ hide_header
hide memory column names
@ show_ascii
show ascii characters for each memory byte
@ colored_output
use terminal colors
Number functions.
String utils.
Memory region description.
Definition: memory_dump.h:97
std::size_t count
Region count.
Definition: memory_dump.h:199
void pushSubRegion(const RegionLayout &sub_region, bool increment_to_parent_size=false)
Appends a layout representing a sub-region of this layout.
Definition: memory_dump.h:145
void clear()
Removes all description.
Definition: memory_dump.h:193
RegionLayout & withOffset(std::size_t offset_in_bytes)
Modifies layout offset.
Definition: memory_dump.h:103
std::size_t sizeInBytes() const
Gets layout size in bytes.
Definition: memory_dump.h:183
void resizeSubRegions(size_t sub_regions_count)
Resizes number of sub-regions of this layout.
Definition: memory_dump.h:186
RegionLayout & withTypeFrom()
Modifies layout base data type based on a given type.
Definition: memory_dump.h:158
DataType type
Base data type.
Definition: memory_dump.h:202
RegionLayout & withColor(const std::string &console_color)
Modifies layout color.
Definition: memory_dump.h:110
std::string color
Region color.
Definition: memory_dump.h:200
RegionLayout & withSizeOf(std::size_t element_count=1)
Modifies layout size based on given type and count.
Definition: memory_dump.h:167
RegionLayout & withType(DataType t)
Modifies layout base data type.
Definition: memory_dump.h:124
RegionLayout & withSize(std::size_t size_in_bytes, std::size_t element_count=1)
Modifies layout size based on given quantities.
Definition: memory_dump.h:176
RegionLayout & withCount(std::size_t region_count)
Modifies layout count.
Definition: memory_dump.h:117
std::vector< RegionLayout > sub_regions
Sub-region descriptions.
Definition: memory_dump.h:201
RegionLayout()=default
Default constructor.
RegionLayout & withSubRegion(const RegionLayout &sub_region, bool increment_to_parent_size=false)
Appends a layout representing a sub-region of this layout.
Definition: memory_dump.h:132
std::size_t field_size_in_bytes
Region size.
Definition: memory_dump.h:198
std::size_t offset
Layout offset in bytes.
Definition: memory_dump.h:197
static HERMES_DEVICE_CALLABLE u8 countHexDigits(T n)
Counts hexadecimal digits.
Definition: numeric.h:161