Compare commits

...

21 Commits

6
.gitignore vendored

@ -1,4 +1,8 @@
*~ *~
test* test*
obj/ obj/
hexview hexview-*
profile/
perf.*
flamegraph.svg

@ -1,30 +1,108 @@
SRC=$(wildcard src/*.cpp) SRC=$(wildcard src/*.cpp)
INCLUDE =include INCLUDE =include
_HX_FEAT?= -DFIXED_ROW_SIZE=24 _HX_FEAT?= -DFIXED_ROW_SIZE=24
CXXFLAGS?= -pipe -O3 -march=native CXXFLAGS_COMMON = -Wall -pedantic --std=gnu++20 $(addprefix -I,$(INCLUDE)) -felide-constructors -fno-strict-aliasing -fno-exceptions
CXXFLAGS+= -flto -felide-constructors -fgraphite LDFLAGS_COMMON = -lfmt
CXXFLAGS+= -Wall -pedantic --std=gnu++20 $(addprefix -I,$(INCLUDE))
LDFLAGS?= -O3 -flto CXXFLAGS_DEFAULT = -pipe -O3 -march=native -fgraphite -flto
LDFLAGS+= -lfmt CXXFLAGS_RELEASE = -fomit-frame-pointer -fmerge-all-constants -fmodulo-sched -funswitch-loops -fsplit-loops
LDFLAGS_DEFAULT = -O3 -flto
CXXFLAGS_DEBUG = -DDEBUG -O0 -g
PROJ = hexview PROJECT = hexview
OBJ = $(addprefix obj/,$(SRC:.cpp=.o)) OBJ = $(addprefix obj/,$(SRC:.cpp=.o))
.PHONY: all ### PGO target specific ###
all: dirs $(PROJ)
# Number of times to run profiling
PGO_ITERATIONS = 512
# Size in kb of training files
PGO_DATASET_SIZE = $$(( 1024 * 4 ))
PGO_OBJ = $(addprefix profile/,$(SRC:.cpp=.o))
PROF_FLAGS = -fprofile-generate
###
.PHONY: release
release: | dirs $(PROJECT)-release
.PHONY: debug
debug: | dirs $(PROJECT)-debug
.PHONY: pgo
pgo: | dirs $(PROJECT)-pgo
dirs: dirs:
@mkdir -p obj/src @mkdir -p obj/src
@mkdir -p profile/src
obj/%.o: %.cpp obj/%.o: %.cpp
$(CXX) -c $< $(_HX_FEAT) $(CXXFLAGS) -o $@ $(LDFLAGS) $(CXX) -c $< $(_HX_FEAT) $(CXXFLAGS) -o $@ $(LDFLAGS)
$(PROJ): $(OBJ) profile/%.o: %.cpp
$(CXX) -c $< $(_HX_FEAT) $(CXXFLAGS) $(PROF_FLAGS) -o $@ $(LDFLAGS) $(PROF_FLAGS)
$(PROJECT)-release: CXXFLAGS?= $(CXXFLAGS_DEFAULT)
$(PROJECT)-release: LDFLAGS?= $(LDFLAGS_DEFAULT)
$(PROJECT)-release: CXXFLAGS+= $(CXXFLAGS_COMMON)
$(PROJECT)-release: CXXFLAGS+= $(CXXFLAGS_RELEASE)
$(PROJECT)-release: LDFLAGS := $(LDFLAGS_COMMON) $(LDFLAGS)
$(PROJECT)-release: $(OBJ)
$(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS) $(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS)
strip $@ strip $@
$(PROJECT)-debug: CXXFLAGS += $(CXXFLAGS_COMMON)
$(PROJECT)-debug: CXXFLAGS += $(CXXFLAGS_DEBUG)
$(PROJECT)-debug: LDFLAGS += $(LDFLAGS_COMMON)
$(PROJECT)-debug: $(OBJ)
$(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS)
pgo-generate: $(PGO_OBJ)
$(CXX) $^ $(CXXFLAGS) $(PROF_FLAGS) -o $@ $(LDFLAGS) $(PROF_FLAGS)
pgo-reset:
find ./profile -name \*.gcda -exec rm {} +
pgo-profile: | pgo-reset pgo-generate
for i in {1..$(PGO_ITERATIONS)}; do \
dd if=/dev/urandom of=/tmp/$(PROJECT)-pgo-test bs=1024 count=$(PGO_DATASET_SIZE) >> /dev/null 2>&1; \
printf "\rIteration $$i / $(PGO_ITERATIONS)"; \
./pgo-generate /tmp/$(PROJECT)-pgo-test > /tmp/$(PROJECT)-pgo-output; \
done
@echo ""
rm /tmp/$(PROJECT)-pgo-test
rm /tmp/$(PROJECT)-pgo-output
rm pgo-generate
pgo-use: CXXFLAGS?= $(CXXFLAGS_DEFAULT)
pgo-use: LDFLAGS?= $(LDFLAGS_DEFAULT)
pgo-use: CXXFLAGS+= $(CXXFLAGS_COMMON)
pgo-use: CXXFLAGS+= $(CXXFLAGS_RELEASE)
pgo-use: LDFLAGS := $(LDFLAGS_COMMON) -lgcov $(LDFLAGS)
pgo-use: PROF_FLAGS = -fprofile-use
pgo-use: $(PGO_OBJ)
$(CXX) $^ $(CXXFLAGS) $(PROF_FLAGS) -o $@ $(LDFLAGS) $(PROF_FLAGS)
$(PROJECT)-pgo: CXXFLAGS?= $(CXXFLAGS_DEFAULT)
$(PROJECT)-pgo: LDFLAGS?= $(LDFLAGS_DEFAULT)
$(PROJECT)-pgo: CXXFLAGS+= $(CXXFLAGS_COMMON)
$(PROJECT)-pgo: CXXFLAGS+= $(CXXFLAGS_RELEASE)
$(PROJECT)-pgo: LDFLAGS := $(LDFLAGS_COMMON) $(LDFLAGS)
$(PROJECT)-pgo: | dirs pgo-profile
find ./profile -name \*.o -exec rm {} +
$(MAKE) pgo-use
mv pgo-use $@
strip $@
clean: clean:
rm -f $(PROJ) rm -f $(PROJECT)-{release,debug,pgo}
rm -rf obj rm -rf obj
rm -rf profile
rm -f pgo-{test,output,generate}

@ -5,4 +5,42 @@
namespace hv { namespace hv {
/// Print a single screen /// Print a single screen
void print_screen(const span<unsigned char> bytes, unsigned long offset); void print_screen(const span<unsigned char> bytes, unsigned long offset);
inline void lookup_hex_string(std::uint64_t x, char* s)
{
static const char digits[513] =
"000102030405060708090a0b0c0d0e0f"
"101112131415161718191a1b1c1d1e1f"
"202122232425262728292a2b2c2d2e2f"
"303132333435363738393a3b3c3d3e3f"
"404142434445464748494a4b4c4d4e4f"
"505152535455565758595a5b5c5d5e5f"
"606162636465666768696a6b6c6d6e6f"
"707172737475767778797a7b7c7d7e7f"
"808182838485868788898a8b8c8d8e8f"
"909192939495969798999a9b9c9d9e9f"
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
int i = 7;
const char *lut = (const char *)digits;
while (i >= 0)
{
int pos = (x & 0xFF) * 2;
char ch = lut[pos];
#ifdef DEBUG
if ( (i*2+1) >=16) std::terminate();
#endif
s[i * 2] = ch;
ch = lut[pos + 1];
s[i * 2 + 1] = ch;
x >>= 8;
i -= 1;
}
}
} }

@ -25,12 +25,12 @@ namespace hv
inline byte& operator[](std::size_t index) inline byte& operator[](std::size_t index)
{ {
if (index>=size) throw "tried to index out of bounds of map"; if (index>=size) std::terminate();
return ((byte*)block0)[index]; return ((byte*)block0)[index];
} }
inline const byte& operator[](std::size_t index) const inline const byte& operator[](std::size_t index) const
{ {
if (index>=size) throw "tried to index out of bounds of map"; if (index>=size) std::terminate();
return ((byte*)block0)[index]; return ((byte*)block0)[index];
} }
@ -64,7 +64,7 @@ namespace hv
inline const byte& operator[](std::size_t index) const inline const byte& operator[](std::size_t index) const
{ {
if (index>=size) throw "tried to index out of bounds of map"; if (index>=size) std::terminate();
return ((byte*)block0)[index]; return ((byte*)block0)[index];
} }

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <exception>
#include <cstdint> #include <cstdint>
#include <cmath> #include <cmath>
@ -19,24 +20,28 @@ namespace hv
inline T& operator[](std::size_t index) inline T& operator[](std::size_t index)
{ {
if (index>=len) throw "tried to index out of bounds of array"; #ifdef DEBUG
if (index>=len) std::terminate(); // "tried to index out of bounds of array";
#endif
return ptr[index]; return ptr[index];
} }
inline const T& operator[](std::size_t index) const inline const T& operator[](std::size_t index) const
{ {
if (index>=len) throw "tried to index out of bounds of array"; #ifdef DEBUG
if (index>=len) std::terminate(); // "tried to index out of bounds of array";
#endif
return ptr[index]; return ptr[index];
} }
inline span<T> slice(std::size_t off, std::size_t len) inline span<T> slice(std::size_t off, std::size_t len)
{ {
len = std::min(this->len, len); len = std::min(this->len, len);
if(len+off>=len || len < off) throw "tried to index out of bounds of array"; if(len+off>=len || len < off) std::terminate(); // "tried to index out of bounds of array";
return span<T>(ptr+off, len-off); return span<T>(ptr+off, len-off);
} }
inline span<T> slice(std::size_t off) inline span<T> slice(std::size_t off)
{ {
if(len+off>=len || len < off) throw "tried to index out of bounds of array"; if(len+off>=len || len < off) std::terminate(); // "tried to index out of bounds of array";
return span<T>(ptr+off, len-off); return span<T>(ptr+off, len-off);
} }

@ -1,5 +1,6 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <span.hpp> #include <span.hpp>
#include <hex.hpp>
const static constexpr char ascii_map[256] = { const static constexpr char ascii_map[256] = {
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
@ -20,6 +21,25 @@ const static constexpr char ascii_map[256] = {
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
}; };
constexpr const static char* hex_map[256] = {
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff",
};
#ifndef FIXED_ROW_SIZE #ifndef FIXED_ROW_SIZE
#include <alloca.h> #include <alloca.h>
#endif #endif
@ -28,35 +48,92 @@ const static constexpr char ascii_map[256] = {
constexpr const int ROW_SZ = FIXED_ROW_SIZE; constexpr const int ROW_SZ = FIXED_ROW_SIZE;
#endif #endif
template<typename... S>
inline __attribute__((always_inline)) void prints(const S&... strings)
{
(fputs((const char*)strings, stdout), ...);
}
inline void hex02(std::uint8_t byte, char buffer[2])
{
buffer[0] =hex_map[byte][0];
buffer[1] =hex_map[byte][1];
}
template<std::size_t N>
inline __attribute__((always_inline)) void print_exact(const char* ar)
{
fwrite(ar, 1, N, stdout);
}
template<std::size_t N>
inline __attribute__((always_inline)) void print_exact(const char (&ar)[N])
{
fwrite(ar, N, 1, stdout);
}
inline static void u64_to_hex(std::uint64_t num, char* s)
{
hv::lookup_hex_string(num, s);
}
namespace hv { namespace hv {
void print_screen(const span<unsigned char> memory, unsigned long offset) void print_screen(const span<unsigned char> memory, unsigned long offset)
{ {
#ifndef FIXED_ROW_SIZE #ifndef FIXED_ROW_SIZE
#define S (const char*) #define S (2+ROW_SZ)
int ROW_SZ = 24; #define P_EX(n, buf) fwrite(buf, 1, n, stdout)
int ROW_SZ = 24;
#else #else
#define S #define S
#define P_EX(n, buf) print_exact(buf)
#endif #endif
fmt::print("0x{:016x} ", offset); char posbuf[3 + 16 + 2];
char ascii[1 + ROW_SZ]; posbuf[0] = '\n';
ascii[ROW_SZ] = 0; posbuf[1] = '0';
std::size_t i=0; posbuf[2] = 'x';
for(;i<memory.size();i++) { #ifdef DEBUG
if (i && i % ROW_SZ == 0) { std::memset(posbuf+3, '0', 16);
fmt::print(" {}", S ascii); #endif
fmt::print("\n0x{:016x} ", i+offset); posbuf[3 + 16] = ' ';
} posbuf[3 + 16 + 1] = ' ';
fmt::print("{:02x} ", memory[i]);
ascii[i%ROW_SZ] = ascii_map[memory[i]]; u64_to_hex(offset, posbuf+3);
} prints(posbuf+1);
if (memory.size() % ROW_SZ != 0)
{ char hxbuf[3] = { ' ', ' ', ' ' };
auto rest = memory.size() % ROW_SZ; std::size_t i=0;
ascii[rest] = 0; char r_ascii[2 + ROW_SZ];
for(std::size_t j=0;j< ROW_SZ - rest;j++) r_ascii[0] = ' ';
fmt::print(" "); r_ascii[1] = ' ';
char* ascii = r_ascii + 2;
} const auto memsize = memory.size();
fmt::print(" {}", S ascii);
} for(;i<memsize;i++) {
if (i && i % ROW_SZ == 0) { //TODO: Change to i +=16 so we don't have to branch here on every byte. Make sure to not overrun buffer tho
P_EX(S, r_ascii);
u64_to_hex(i+offset, posbuf+3);
print_exact(posbuf);
}
unsigned char idx = memory[i];
hex02(idx, hxbuf);
print_exact(hxbuf);
ascii[i%ROW_SZ] = ascii_map[idx];
}
if (memsize % ROW_SZ != 0)
{
auto rest = memsize % ROW_SZ;
ascii[rest] = 0;
constexpr const char output[3] = { ' ', ' ', ' ' };
for(std::size_t j=0;j< ROW_SZ - rest;j++)
print_exact(output);
prints((const char*) r_ascii);
}
else {
P_EX(S, r_ascii);
}
}
} }

@ -9,12 +9,12 @@ namespace hv
memory_map::memory_map(std::uint64_t size, int&& fd) noexcept memory_map::memory_map(std::uint64_t size, int&& fd) noexcept
: is_cow(false), size(size), fd(fd), block0(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) : is_cow(false), size(size), fd(fd), block0(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))
{ {
if(!block0) throw "memory_map failed"; if(!block0) std::terminate();
} }
memory_map::memory_map(std::uint64_t size, int&& fd, bool _cow) noexcept memory_map::memory_map(std::uint64_t size, int&& fd, bool _cow) noexcept
: is_cow(true), size(size), fd(fd), block0(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) : is_cow(true), size(size), fd(fd), block0(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0))
{ {
if(!block0) throw "memory_map::cow failed"; if(!block0) std::terminate();
} }
memory_map::memory_map(std::uint64_t size, const char* file) noexcept memory_map::memory_map(std::uint64_t size, const char* file) noexcept
: memory_map(size, open(file, O_RDWR)) {} : memory_map(size, open(file, O_RDWR)) {}

Loading…
Cancel
Save