SchOS/cmd/schdbg/main.c
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

139 lines
4 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// schdbg -- ask the kernel a question.
//
// docs/architecture/debugging.md, milestone D2. The kernel half is /dev/dbg: write a
// line, read the answer. This is the half that makes that pleasant, and it is
// deliberately almost nothing -- because the protocol is lines of text, and a
// protocol whose client has to be clever is a protocol you cannot use when the
// client will not start.
//
// schdbg the views this kernel publishes
// schdbg dma one of them
// schdbg all all of them
// schdbg -i a prompt, for when you are asking several
//
// Everything here works equally well as:
//
// echo dma > /dev/dbg && cat /dev/dbg
//
// which is the point, and is what you fall back to on a system where this
// program is one of the things that is broken.
#include "core/types.h"
#include "uapi/user.h"
#include "uapi/fcntl.h"
static void
puts_(const char *s)
{
int n = 0;
while (s[n]) n++;
write(1, (void *)s, n);
}
// One question, one answer.
//
// The descriptor is opened fresh for each, because the answer is read from
// offset zero and the kernel replaces it on every write. Reusing one open
// descriptor across two questions would read the second answer from wherever
// the first one left off.
static int
ask(const char *cmd)
{
char buf[512];
int fd, n;
fd = open("/dev/dbg", O_RDWR);
if (fd < 0) {
puts_("schdbg: cannot open /dev/dbg\n");
return -1;
}
if (cmd) {
int len = 0;
while (cmd[len]) len++;
if (write(fd, (void *)cmd, len) != len) {
puts_("schdbg: the kernel would not take that command\n");
close(fd);
return -1;
}
// Back to the start. Writing moved this descriptor's offset forward by
// the length of the command, and the answer begins at zero -- so
// without this the reply arrives with its first few characters missing,
// which is subtle enough to look like a formatting bug in the view.
lseek(fd, 0, 0); /* SEEK_SET */
}
while ((n = read(fd, buf, sizeof(buf))) > 0)
write(1, buf, n);
close(fd);
return 0;
}
// A line from the terminal, without the newline. Returns 0 at end of input.
static int
readline(char *buf, int cap)
{
int n = 0;
char c;
for (;;) {
int r = read(0, &c, 1);
if (r <= 0)
return n > 0;
if (c == '\n') {
buf[n] = 0;
return 1;
}
if (n < cap - 1)
buf[n++] = c;
}
}
int
main(int argc, char *argv[])
{
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'i') {
char line[128];
puts_("schdbg: ask the kernel something. 'help' lists the views, "
"'quit' leaves.\n");
for (;;) {
puts_("dbg> ");
if (!readline(line, sizeof(line)))
break;
if (line[0] == 0)
continue;
if (line[0] == 'q' && line[1] == 'u' && line[2] == 'i' &&
line[3] == 't' && line[4] == 0)
break;
ask(line);
}
return 0;
}
// No argument means "what can I ask", which is the question someone has
// the first time and the one an empty response would not answer.
if (argc < 2)
return ask("help") < 0 ? 1 : 0;
// Everything after the program name, rejoined with single spaces. The
// shell split it and the kernel wants a line; `schdbg vma 4` has to arrive
// as "vma 4" or the argument is silently dropped and the view quietly
// ignores the narrowing it was asked for.
{
char line[128];
int n = 0;
for (int i = 1; i < argc; i++) {
if (i > 1 && n < (int)sizeof(line) - 1)
line[n++] = ' ';
for (int j = 0; argv[i][j] && n < (int)sizeof(line) - 1; j++)
line[n++] = argv[i][j];
}
line[n] = 0;
return ask(line) < 0 ? 1 : 0;
}
}