mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-08 20:28:30 +00:00
Add tool for viewing memory
https://justine.storage.googleapis.com/memzoom/index.html
This commit is contained in:
parent
2d80bbc802
commit
9fe95ef12b
22 changed files with 1511 additions and 1028 deletions
940
tool/viz/memzoom.c
Normal file
940
tool/viz/memzoom.c
Normal file
|
@ -0,0 +1,940 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ This program is free software; you can redistribute it and/or modify │
|
||||
│ it under the terms of the GNU General Public License as published by │
|
||||
│ the Free Software Foundation; version 2 of the License. │
|
||||
│ │
|
||||
│ This program is distributed in the hope that it will be useful, but │
|
||||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||||
│ General Public License for more details. │
|
||||
│ │
|
||||
│ You should have received a copy of the GNU General Public License │
|
||||
│ along with this program; if not, write to the Free Software │
|
||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/scale/cdecimate2xuint8x8.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/hilbert.h"
|
||||
#include "libc/bits/morton.h"
|
||||
#include "libc/bits/safemacros.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/ioctl.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/struct/termios.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/conv/conv.h"
|
||||
#include "libc/conv/itoa.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/nexgen32e/bsf.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/tpenc.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/unicode/unicode.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
|
||||
#define USAGE \
|
||||
" [-hznmH] [-p PID] [PATH]\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
Memory Viewer\n\
|
||||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-h help\n\
|
||||
-z zoom\n\
|
||||
-w white bg color\n\
|
||||
-m morton ordering\n\
|
||||
-H hilbert ordering\n\
|
||||
-n natural scrolling\n\
|
||||
-p PID shows process virtual memory\n\
|
||||
-f INT frames per second [default 10]\n\
|
||||
\n\
|
||||
SHORTCUTS\n\
|
||||
\n\
|
||||
z or + zoom\n\
|
||||
Z or - unzoom\n\
|
||||
ctrl+wheel zoom point\n\
|
||||
wheel scroll\n\
|
||||
l linearize\n\
|
||||
m mortonize\n\
|
||||
h hilbertify\n\
|
||||
n next mapping\n\
|
||||
N next mapping ending\n\
|
||||
p prev mapping\n\
|
||||
P prev mapping ending\n\
|
||||
k up\n\
|
||||
j down\n\
|
||||
b page up\n\
|
||||
space page down\n\
|
||||
g home\n\
|
||||
G end\n\
|
||||
q quit\n\
|
||||
\n"
|
||||
|
||||
#define CTRL(C) ((C) ^ 0100)
|
||||
|
||||
#define MAXZOOM 14
|
||||
#define COLOR 253
|
||||
|
||||
#define LINEAR 0
|
||||
#define MORTON 1
|
||||
#define HILBERT 2
|
||||
|
||||
#define INTERRUPTED 0x1
|
||||
#define RESIZED 0x2
|
||||
|
||||
#define MOUSE_LEFT_DOWN 0
|
||||
#define MOUSE_MIDDLE_DOWN 1
|
||||
#define MOUSE_RIGHT_DOWN 2
|
||||
#define MOUSE_LEFT_UP 4
|
||||
#define MOUSE_MIDDLE_UP 5
|
||||
#define MOUSE_RIGHT_UP 6
|
||||
#define MOUSE_LEFT_DRAG 32
|
||||
#define MOUSE_MIDDLE_DRAG 33
|
||||
#define MOUSE_RIGHT_DRAG 34
|
||||
#define MOUSE_WHEEL_UP 64
|
||||
#define MOUSE_WHEEL_DOWN 65
|
||||
#define MOUSE_CTRL_WHEEL_UP 80
|
||||
#define MOUSE_CTRL_WHEEL_DOWN 81
|
||||
|
||||
struct Ranges {
|
||||
long i;
|
||||
struct Range {
|
||||
long a;
|
||||
long b;
|
||||
} p[512];
|
||||
};
|
||||
|
||||
static const signed char kThePerfectKernel[8] = {-1, -3, 3, 17, 17, 3, -3, -1};
|
||||
|
||||
static bool white;
|
||||
static bool natural;
|
||||
static bool mousemode;
|
||||
|
||||
static int fd;
|
||||
static int pid;
|
||||
static int out;
|
||||
static int fps;
|
||||
static int zoom;
|
||||
static int order;
|
||||
static int action;
|
||||
|
||||
static long tyn;
|
||||
static long txn;
|
||||
static long size;
|
||||
static long offset;
|
||||
static long lowest;
|
||||
static long highest;
|
||||
static long canvassize;
|
||||
static long buffersize;
|
||||
static long displaysize;
|
||||
|
||||
static char *buffer;
|
||||
static uint8_t *canvas;
|
||||
|
||||
static struct stat st;
|
||||
static struct Ranges ranges;
|
||||
static struct termios oldterm;
|
||||
|
||||
static char path[PATH_MAX];
|
||||
static char mapspath[PATH_MAX];
|
||||
|
||||
static int Write(const char *s) {
|
||||
return write(out, s, strlen(s));
|
||||
}
|
||||
|
||||
static void HideCursor(void) {
|
||||
Write("\e[?25l");
|
||||
}
|
||||
|
||||
static void ShowCursor(void) {
|
||||
Write("\e[?25h");
|
||||
}
|
||||
|
||||
static void EnableMouse(void) {
|
||||
mousemode = true;
|
||||
Write("\e[?1000;1002;1015;1006h");
|
||||
}
|
||||
|
||||
static void DisableMouse(void) {
|
||||
mousemode = false;
|
||||
Write("\e[?1000;1002;1015;1006l");
|
||||
}
|
||||
|
||||
static void LeaveScreen(void) {
|
||||
Write("\e[H\e[J");
|
||||
}
|
||||
|
||||
static void GetTtySize(void) {
|
||||
struct winsize wsize;
|
||||
wsize.ws_row = tyn + 1;
|
||||
wsize.ws_col = txn;
|
||||
getttysize(out, &wsize);
|
||||
tyn = MAX(2, wsize.ws_row) - 1;
|
||||
txn = MAX(17, wsize.ws_col) - 16;
|
||||
tyn = rounddown2pow(tyn);
|
||||
txn = rounddown2pow(txn);
|
||||
tyn = MIN(tyn, txn);
|
||||
}
|
||||
|
||||
static void EnableRaw(void) {
|
||||
struct termios term;
|
||||
memcpy(&term, &oldterm, sizeof(term));
|
||||
term.c_cc[VMIN] = 1;
|
||||
term.c_cc[VTIME] = 1;
|
||||
term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON);
|
||||
term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL);
|
||||
term.c_cflag &= ~(CSIZE | PARENB);
|
||||
term.c_cflag |= CS8;
|
||||
term.c_iflag |= IUTF8;
|
||||
ioctl(out, TCSETS, &term);
|
||||
}
|
||||
|
||||
static void OnExit(void) {
|
||||
LeaveScreen();
|
||||
ShowCursor();
|
||||
DisableMouse();
|
||||
ioctl(out, TCSETS, &oldterm);
|
||||
}
|
||||
|
||||
static void OnSigInt(int sig, struct siginfo *sa, struct ucontext *uc) {
|
||||
action |= INTERRUPTED;
|
||||
}
|
||||
|
||||
static void OnSigWinch(int sig, struct siginfo *sa, struct ucontext *uc) {
|
||||
action |= RESIZED;
|
||||
}
|
||||
|
||||
static void Setup(void) {
|
||||
tyn = 80;
|
||||
txn = 24;
|
||||
action = RESIZED;
|
||||
ioctl(out, TCGETS, &oldterm);
|
||||
HideCursor();
|
||||
EnableRaw();
|
||||
EnableMouse();
|
||||
atexit(OnExit);
|
||||
sigaction(SIGINT, &(struct sigaction){.sa_sigaction = OnSigInt}, NULL);
|
||||
sigaction(SIGWINCH, &(struct sigaction){.sa_sigaction = OnSigWinch}, NULL);
|
||||
}
|
||||
|
||||
static noreturn void FailPath(const char *s, int rc) {
|
||||
Write("error: ");
|
||||
Write(s);
|
||||
Write(": ");
|
||||
Write(path);
|
||||
Write("\n");
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void SetExtent(long lo, long hi) {
|
||||
lowest = lo;
|
||||
highest = hi;
|
||||
offset = MIN(hi, MAX(lo, offset));
|
||||
}
|
||||
|
||||
static void Open(void) {
|
||||
int err;
|
||||
if ((fd = open(path, O_RDONLY)) == -1) {
|
||||
FailPath("open() failed", errno);
|
||||
}
|
||||
fstat(fd, &st);
|
||||
size = st.st_size;
|
||||
SetExtent(0, size);
|
||||
}
|
||||
|
||||
static void *Allocate(size_t n) {
|
||||
return mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1,
|
||||
0);
|
||||
}
|
||||
|
||||
static void SetupCanvas(void) {
|
||||
if (canvassize) {
|
||||
munmap(canvas, canvassize);
|
||||
munmap(buffer, buffersize);
|
||||
}
|
||||
displaysize = ROUNDUP(ROUNDUP(tyn * txn * (1ul << zoom), 16), 1ul << zoom);
|
||||
canvassize = ROUNDUP(displaysize, FRAMESIZE);
|
||||
buffersize = ROUNDUP(tyn * txn * 16 + PAGESIZE, FRAMESIZE);
|
||||
canvas = Allocate(canvassize);
|
||||
buffer = Allocate(buffersize);
|
||||
}
|
||||
|
||||
static long IndexSquare(long y, long x) {
|
||||
switch (order) {
|
||||
case LINEAR:
|
||||
return y * txn + x;
|
||||
case MORTON:
|
||||
return morton(y, x);
|
||||
case HILBERT:
|
||||
return hilbert(txn, y, x);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static long Index(long y, long x) {
|
||||
long i;
|
||||
if (order == LINEAR) {
|
||||
i = 0;
|
||||
} else {
|
||||
i = x / tyn;
|
||||
x = x % tyn;
|
||||
}
|
||||
return i * tyn * tyn + IndexSquare(y, x);
|
||||
}
|
||||
|
||||
static void PreventBufferbloat(void) {
|
||||
long double now, rate;
|
||||
static long double last;
|
||||
now = nowl();
|
||||
rate = 1. / fps;
|
||||
if (now - last < rate) {
|
||||
dsleep(rate - (now - last));
|
||||
}
|
||||
last = now;
|
||||
}
|
||||
|
||||
static bool HasPendingInput(void) {
|
||||
struct pollfd fds[1];
|
||||
fds[0].fd = 0;
|
||||
fds[0].events = POLLIN;
|
||||
fds[0].revents = 0;
|
||||
poll(fds, ARRAYLEN(fds), 0);
|
||||
return fds[0].revents & (POLLIN | POLLERR);
|
||||
}
|
||||
|
||||
static int GetCurrentRange(void) {
|
||||
int i;
|
||||
if (ranges.i) {
|
||||
for (i = 0; i < ranges.i; ++i) {
|
||||
if (offset < ranges.p[i].a) return MAX(0, i - 1);
|
||||
if (offset < ranges.p[i].b) return i;
|
||||
}
|
||||
return ranges.i - 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void Move(long d) {
|
||||
offset = MIN(highest, MAX(lowest, ROUNDDOWN(offset + d, 1L << zoom)));
|
||||
}
|
||||
|
||||
static void SetZoom(long y, long x, int d) {
|
||||
long a, b, i;
|
||||
if ((0 <= y && y < tyn) && (0 <= x && x < txn)) {
|
||||
i = Index(y, x);
|
||||
a = zoom;
|
||||
b = MIN(MAXZOOM, MAX(0, a + d));
|
||||
zoom = b;
|
||||
Move(i * (1L << a) - i * (1L << b));
|
||||
SetupCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
static void OnZoom(long y, long x) {
|
||||
SetZoom(y, x, +1);
|
||||
}
|
||||
|
||||
static void OnUnzoom(long y, long x) {
|
||||
SetZoom(y, x, -1);
|
||||
}
|
||||
|
||||
static void OnUp(void) {
|
||||
Move(-(txn * (1l << zoom)));
|
||||
}
|
||||
|
||||
static void OnDown(void) {
|
||||
Move(txn * (1l << zoom));
|
||||
}
|
||||
|
||||
static void OnPageUp(void) {
|
||||
Move(-(txn * (tyn - 2) * (1l << zoom)));
|
||||
}
|
||||
|
||||
static void OnPageDown(void) {
|
||||
Move(txn * (tyn - 2) * (1l << zoom));
|
||||
}
|
||||
|
||||
static void OnHome(void) {
|
||||
offset = lowest;
|
||||
}
|
||||
|
||||
static void OnEnd(void) {
|
||||
offset = MAX(lowest, highest - txn * tyn * (1l << zoom));
|
||||
}
|
||||
|
||||
static void OnLinear(void) {
|
||||
order = LINEAR;
|
||||
GetTtySize();
|
||||
SetupCanvas();
|
||||
}
|
||||
|
||||
static void OnMorton(void) {
|
||||
order = MORTON;
|
||||
SetupCanvas();
|
||||
}
|
||||
|
||||
static void OnHilbert(void) {
|
||||
order = HILBERT;
|
||||
SetupCanvas();
|
||||
}
|
||||
|
||||
static void OnNext(void) {
|
||||
int i;
|
||||
if ((i = GetCurrentRange()) != -1) {
|
||||
if (i + 1 < ranges.i) {
|
||||
offset = ranges.p[i + 1].a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnPrev(void) {
|
||||
int i;
|
||||
if ((i = GetCurrentRange()) != -1) {
|
||||
if (i) {
|
||||
offset = ranges.p[i - 1].a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnNextEnd(void) {
|
||||
long i, n;
|
||||
if ((i = GetCurrentRange()) != -1) {
|
||||
n = tyn * txn * (1L << zoom);
|
||||
if (offset < ranges.p[i].b - n) {
|
||||
offset = ranges.p[i].b - n;
|
||||
} else if (i + 1 < ranges.i) {
|
||||
offset = MAX(ranges.p[i + 1].a, ranges.p[i + 1].b - n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnPrevEnd(void) {
|
||||
long i, n;
|
||||
if ((i = GetCurrentRange()) != -1) {
|
||||
n = tyn * txn * (1L << zoom);
|
||||
if (i) {
|
||||
offset = MAX(ranges.p[i - 1].a, ranges.p[i - 1].b - n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnMouse(char *p) {
|
||||
int e, x, y;
|
||||
struct Panel *ep;
|
||||
e = strtol(p, &p, 10);
|
||||
if (*p == ';') ++p;
|
||||
x = min(txn, max(1, strtol(p, &p, 10))) - 1;
|
||||
if (*p == ';') ++p;
|
||||
y = min(tyn, max(1, strtol(p, &p, 10))) - 1;
|
||||
e |= (*p == 'm') << 2;
|
||||
switch (e) {
|
||||
case MOUSE_WHEEL_UP:
|
||||
if (natural) {
|
||||
OnDown();
|
||||
OnDown();
|
||||
OnDown();
|
||||
} else {
|
||||
OnUp();
|
||||
OnUp();
|
||||
OnUp();
|
||||
}
|
||||
break;
|
||||
case MOUSE_WHEEL_DOWN:
|
||||
if (natural) {
|
||||
OnUp();
|
||||
OnUp();
|
||||
OnUp();
|
||||
} else {
|
||||
OnDown();
|
||||
OnDown();
|
||||
OnDown();
|
||||
}
|
||||
break;
|
||||
case MOUSE_CTRL_WHEEL_UP:
|
||||
if (natural) {
|
||||
OnZoom(y, x);
|
||||
} else {
|
||||
OnUnzoom(y, x);
|
||||
}
|
||||
break;
|
||||
case MOUSE_CTRL_WHEEL_DOWN:
|
||||
if (natural) {
|
||||
OnUnzoom(y, x);
|
||||
} else {
|
||||
OnZoom(y, x);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ReadKeyboard(void) {
|
||||
char buf[32], *p = buf;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (readansi(0, buf, sizeof(buf)) == -1) {
|
||||
if (errno == EINTR) return;
|
||||
exit(errno);
|
||||
}
|
||||
switch (*p++) {
|
||||
case 'q':
|
||||
exit(0);
|
||||
case '+':
|
||||
case 'z':
|
||||
OnZoom(0, 0);
|
||||
break;
|
||||
case '-':
|
||||
case 'Z':
|
||||
OnUnzoom(0, 0);
|
||||
break;
|
||||
case 'b':
|
||||
OnPageUp();
|
||||
break;
|
||||
case 'n':
|
||||
OnNext();
|
||||
break;
|
||||
case 'p':
|
||||
OnPrev();
|
||||
break;
|
||||
case 'N':
|
||||
OnNextEnd();
|
||||
break;
|
||||
case 'P':
|
||||
OnPrevEnd();
|
||||
break;
|
||||
case ' ':
|
||||
case CTRL('V'):
|
||||
OnPageDown();
|
||||
break;
|
||||
case 'g':
|
||||
OnHome();
|
||||
break;
|
||||
case 'G':
|
||||
OnEnd();
|
||||
break;
|
||||
case 'k':
|
||||
case CTRL('P'):
|
||||
OnUp();
|
||||
break;
|
||||
case 'j':
|
||||
case CTRL('N'):
|
||||
OnDown();
|
||||
break;
|
||||
case 'l':
|
||||
OnLinear();
|
||||
break;
|
||||
case 'm':
|
||||
if (order == MORTON) {
|
||||
OnLinear();
|
||||
} else {
|
||||
OnMorton();
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
if (mousemode) {
|
||||
DisableMouse();
|
||||
} else {
|
||||
EnableMouse();
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
if (order == HILBERT) {
|
||||
OnLinear();
|
||||
} else {
|
||||
OnHilbert();
|
||||
}
|
||||
break;
|
||||
case '\e':
|
||||
switch (*p++) {
|
||||
case 'v':
|
||||
OnPageUp();
|
||||
break;
|
||||
case '[':
|
||||
switch (*p++) {
|
||||
case '<':
|
||||
OnMouse(p);
|
||||
break;
|
||||
case 'A':
|
||||
OnUp();
|
||||
break;
|
||||
case 'B':
|
||||
OnDown();
|
||||
break;
|
||||
case 'F':
|
||||
OnEnd();
|
||||
break;
|
||||
case 'H':
|
||||
OnHome();
|
||||
break;
|
||||
case '1':
|
||||
switch (*p++) {
|
||||
case '~':
|
||||
OnHome();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '4':
|
||||
switch (*p++) {
|
||||
case '~':
|
||||
OnEnd();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '5':
|
||||
switch (*p++) {
|
||||
case '~':
|
||||
OnPageUp();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '6':
|
||||
switch (*p++) {
|
||||
case '~':
|
||||
OnPageDown();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '7':
|
||||
switch (*p++) {
|
||||
case '~':
|
||||
OnHome();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '8':
|
||||
switch (*p++) {
|
||||
case '~':
|
||||
OnEnd();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadRanges(void) {
|
||||
char b[512];
|
||||
struct Range range;
|
||||
int i, t, n, fd, err;
|
||||
if ((fd = open(mapspath, O_RDONLY)) == -1) {
|
||||
err = errno;
|
||||
Write("error: process died\n");
|
||||
exit(err);
|
||||
}
|
||||
t = 0;
|
||||
range.a = 0;
|
||||
range.b = 0;
|
||||
ranges.i = 0;
|
||||
for (;;) {
|
||||
if ((n = read(fd, b, sizeof(b))) == -1) exit(1);
|
||||
if (!n) break;
|
||||
for (i = 0; i < n; ++i) {
|
||||
switch (t) {
|
||||
case 0:
|
||||
if (isxdigit(b[i])) {
|
||||
range.a <<= 4;
|
||||
range.a += hextoint(b[i]);
|
||||
} else if (b[i] == '-') {
|
||||
t = 1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (isxdigit(b[i])) {
|
||||
range.b <<= 4;
|
||||
range.b += hextoint(b[i]);
|
||||
} else if (b[i] == ' ') {
|
||||
t = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (b[i] == '\n') {
|
||||
if (ranges.i < ARRAYLEN(ranges.p)) {
|
||||
ranges.p[ranges.i++] = range;
|
||||
}
|
||||
range.a = 0;
|
||||
range.b = 0;
|
||||
t = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
if (ranges.i) {
|
||||
SetExtent(ranges.p[0].a, ranges.p[ranges.i - 1].b);
|
||||
} else {
|
||||
SetExtent(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int InvertXtermGreyscale(int x) {
|
||||
return -(x - 232) + 255;
|
||||
}
|
||||
|
||||
static void Render(void) {
|
||||
char *p;
|
||||
int c, fg2, rc, fg;
|
||||
long i, y, x, w, n, got;
|
||||
p = buffer;
|
||||
p = stpcpy(p, "\e[H");
|
||||
for (y = 0; y < tyn; ++y) {
|
||||
fg = -1;
|
||||
for (x = 0; x < txn; ++x) {
|
||||
c = canvas[Index(y, x)];
|
||||
if (c < 32) {
|
||||
fg2 = 237 + c * ((COLOR - 237) / 32.);
|
||||
} else if (c >= 232) {
|
||||
fg2 = COLOR + (c - 232) * ((255 - COLOR) / (256. - 232));
|
||||
} else {
|
||||
fg2 = COLOR;
|
||||
}
|
||||
if (fg2 != fg) {
|
||||
fg = fg2;
|
||||
if (white) {
|
||||
fg = InvertXtermGreyscale(fg);
|
||||
}
|
||||
p = stpcpy(p, "\e[38;5;");
|
||||
p += int64toarray_radix10(fg, p);
|
||||
*p++ = 'm';
|
||||
}
|
||||
w = tpenc(kCp437[c]);
|
||||
do {
|
||||
*p++ = w & 0xff;
|
||||
w >>= 8;
|
||||
} while (w);
|
||||
}
|
||||
p = stpcpy(p, "\e[0m ");
|
||||
p += uint64toarray_radix16(offset + y * txn * (1ul << zoom), p);
|
||||
p = stpcpy(p, "\e[K\r\n");
|
||||
}
|
||||
p = stpcpy(p, "\e[7m\e[K");
|
||||
n = strlen(path);
|
||||
if (n > txn - 3 - 1 - 7) {
|
||||
p = mempcpy(p, path, txn - 1 - 7 - 3);
|
||||
p = stpcpy(p, "...");
|
||||
} else {
|
||||
p = stpcpy(p, path);
|
||||
for (i = n; i < txn - 1 - 7; ++i) {
|
||||
*p++ = ' ';
|
||||
}
|
||||
}
|
||||
p = stpcpy(p, " memzoom\e[0m ");
|
||||
if (!pid) {
|
||||
p += uint64toarray_radix10(MIN(offset / (long double)size * 100, 100), p);
|
||||
p = stpcpy(p, "%-");
|
||||
p += uint64toarray_radix10(
|
||||
MIN((offset + tyn * txn * (1l << zoom)) / (long double)size * 100, 100),
|
||||
p);
|
||||
p = stpcpy(p, "% ");
|
||||
}
|
||||
p += uint64toarray_radix10(1L << zoom, p);
|
||||
p = stpcpy(p, "x\e[J");
|
||||
PreventBufferbloat();
|
||||
for (i = 0, n = p - buffer; i < n; i += got) {
|
||||
got = 0;
|
||||
if ((rc = write(out, buffer + i, n - i)) == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
exit(errno);
|
||||
}
|
||||
got = rc;
|
||||
}
|
||||
}
|
||||
|
||||
static void Zoom(long have) {
|
||||
long i, n, r;
|
||||
n = canvassize;
|
||||
for (i = 0; i < zoom; ++i) {
|
||||
cDecimate2xUint8x8(n, canvas, kThePerfectKernel);
|
||||
n >>= 1;
|
||||
}
|
||||
if (n < tyn * txn) {
|
||||
memset(canvas + n, 0, canvassize - n);
|
||||
}
|
||||
if (have != -1) {
|
||||
n = have / (1L << zoom);
|
||||
i = n / txn;
|
||||
r = n % txn;
|
||||
if (r) ++i;
|
||||
if (order == LINEAR) {
|
||||
for (; i < tyn; ++i) {
|
||||
canvas[txn * i] = '~';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FileZoom(void) {
|
||||
long have;
|
||||
have = MIN(displaysize, size - offset);
|
||||
have = pread(fd, canvas, have, offset);
|
||||
have = MAX(0, have);
|
||||
memset(canvas + have, 0, canvassize - have);
|
||||
Zoom(have);
|
||||
Render();
|
||||
}
|
||||
|
||||
static void RangesZoom(void) {
|
||||
long a, b, c, d, i;
|
||||
LoadRanges();
|
||||
memset(canvas, 1, canvassize);
|
||||
a = offset;
|
||||
b = MIN(highest, offset + tyn * txn * (1ul << zoom));
|
||||
for (i = 0; i < ranges.i; ++i) {
|
||||
if ((a >= ranges.p[i].a && a < ranges.p[i].b) ||
|
||||
(b >= ranges.p[i].a && b < ranges.p[i].b) ||
|
||||
(a < ranges.p[i].a && b >= ranges.p[i].b)) {
|
||||
c = MAX(a, ranges.p[i].a);
|
||||
d = MIN(b, ranges.p[i].b);
|
||||
pread(fd, canvas + (c - offset), d - c, c);
|
||||
}
|
||||
}
|
||||
Zoom(-1);
|
||||
Render();
|
||||
}
|
||||
|
||||
static void MemZoom(void) {
|
||||
char *p;
|
||||
int c, fg2, rc, fg;
|
||||
long i, n, r, w, y, x, got, have;
|
||||
do {
|
||||
if (action & INTERRUPTED) {
|
||||
break;
|
||||
}
|
||||
if (action & RESIZED) {
|
||||
GetTtySize();
|
||||
SetupCanvas();
|
||||
action &= ~RESIZED;
|
||||
}
|
||||
if (HasPendingInput()) {
|
||||
ReadKeyboard();
|
||||
continue;
|
||||
}
|
||||
if (pid) {
|
||||
RangesZoom();
|
||||
} else {
|
||||
FileZoom();
|
||||
}
|
||||
} while (!(action & INTERRUPTED));
|
||||
}
|
||||
|
||||
static noreturn void PrintUsage(int rc) {
|
||||
Write("SYNOPSIS\n\n ");
|
||||
Write(program_invocation_name);
|
||||
Write(USAGE);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
char *p;
|
||||
fps = 10;
|
||||
while ((opt = getopt(argc, argv, "hzHnwf:p:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'z':
|
||||
++zoom;
|
||||
break;
|
||||
case 'n':
|
||||
natural = true;
|
||||
break;
|
||||
case 'm':
|
||||
order = MORTON;
|
||||
break;
|
||||
case 'H':
|
||||
order = HILBERT;
|
||||
break;
|
||||
case 'w':
|
||||
white = true;
|
||||
break;
|
||||
case 'f':
|
||||
fps = strtol(optarg, NULL, 0);
|
||||
fps = MAX(1, fps);
|
||||
break;
|
||||
case 'p':
|
||||
if (strcmp(optarg, "self") == 0) {
|
||||
pid = getpid();
|
||||
} else {
|
||||
pid = strtol(optarg, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
PrintUsage(EXIT_SUCCESS);
|
||||
default:
|
||||
PrintUsage(EX_USAGE);
|
||||
}
|
||||
}
|
||||
if (pid) {
|
||||
p = stpcpy(path, "/proc/");
|
||||
p += int64toarray_radix10(pid, p);
|
||||
stpcpy(p, "/mem");
|
||||
p = stpcpy(mapspath, "/proc/");
|
||||
p += int64toarray_radix10(pid, p);
|
||||
stpcpy(p, "/maps");
|
||||
} else {
|
||||
if (optind == argc) {
|
||||
PrintUsage(EX_USAGE);
|
||||
}
|
||||
if (!memccpy(path, argv[optind], '\0', sizeof(path))) {
|
||||
PrintUsage(EX_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (!NoDebug()) showcrashreports();
|
||||
out = 1;
|
||||
GetOpts(argc, argv);
|
||||
Open();
|
||||
Setup();
|
||||
MemZoom();
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue