// 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 "types.h" #include "user.h" #include "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; } }