#pragma once #include "types.h" struct sbuf; // Asking the kernel a question. // // docs/architecture/debugging.md milestone D2. The thing this exists to end is the // ninety-second loop: add a cprintf, rebuild, boot, read the serial log, and // learn the answer to exactly the one question you thought of before the // rebuild. // // The interesting part is not the views. It is that a subsystem publishes its // own state with **one line at the bottom of the file that owns the data**, and // nothing central has to be edited to add one: // // dbg_register("dma", dma_dump, "the DMA arena's free extents"); // // That is the difference between something that stays current and something // that does not. /proc/sysrq is the version that does not -- a switch in one // file with a letter per action, which is why after two years it holds three // self-tests and no state at all. // // Views run in ordinary process context, from a write to /dev/dbg or from // sysrq, so they may take locks. **They must not be called from panic().** // The argument is whatever followed the view's name on the command line, or // "" when there was none. Most views ignore it; the ones that can produce a // hundred lines use it to narrow -- `vma 4` rather than `vma`. It is also the // shape milestone D3 needs, since `whose ` is nothing but an argument. typedef void (*dbg_view_fn)(struct sbuf *out, const char *arg); // Publish a view. `what` is one line, shown by `help`. // // Safe to call from any subsystem's init, in any order: the table is static // and needs no initialisation of its own. void dbg_register(const char *name, dbg_view_fn fn, const char *what); // The same, for a view that **changes something**. // // Nearly every view only reads, and the few that do not are worth separating // because of where this can now be reached from. `watch 0xffff...` arms a debug // register; that is a legitimate thing for a debugger to do and an illegitimate // thing for a stranger on a TCP socket to do, and milestone D5 put a TCP socket // in front of it. // // The flag lives on the registration and not in a list somewhere, for the same // reason the registration exists at all: a central list of which views are // dangerous is a list that goes stale, and it goes stale in the direction where // a new view is quietly treated as safe. void dbg_register_rw(const char *name, dbg_view_fn fn, const char *what); // Answer only the views that read. // // `/dev/dbgro` is the same device on minor 1. The minor is the one piece of // per-open state a character device already sees, so *which node you opened* is // the permission -- there is nothing to latch, nothing to remember between the // write and the read, and no way to escalate from one descriptor to the other. // // `schdbgd` opens that one and never the other, so nothing reachable over the // network can arm a watchpoint. See cmd/schdbgd. int dbg_command_ro(const char *line, struct sbuf *out, int ro); // --- whose ----------------------------------------------------------- // // The query the views cannot answer between them: *who owns this page?* // // Each view speaks for one subsystem, which is what keeps them cheap and // current -- and it is exactly why none of them can see the bug that matters. // A page on the DMA arena's free list looks fine from dma.c. The same page on // kalloc's free list looks fine from kalloc.c. That it is on both is a fact no // single subsystem holds, and it is the bug that cost this project a day. // // So `whose` asks every subsystem about one page and counts the answers. The // contributors are registered the same way views are, for the same reason: // adding one is a line in the file that owns the data. struct whose { uint64 phys; // the page being asked about, page-aligned uint64 off; // how far into it the original address was int owners; // allocators and reserved regions claiming it int free_owners; // of those, how many believe it is unused int mappings; // user page tables mapping it }; typedef void (*dbg_whose_fn)(struct sbuf *out, struct whose *w); // Publish a claimant. `name` is the column it prints under. void dbg_register_whose(const char *name, dbg_whose_fn fn); // Run one command line and render the answer. Returns the number of bytes // written into `out`. // // The protocol is lines of text -- see docs/architecture/debugging.md for why that and // not a typed binary format. Commands: // // help what this kernel can be asked // all every view, in registration order // [arg] that view, optionally narrowed // whose every subsystem's claim on one page int dbg_command(const char *line, struct sbuf *out); // Every view, straight to the console. This is what sysrq 'g' runs, and it is // the level-0 path from the plan: no process, no filesystem, no client. void dbg_dump_all(void); #define DBG_MAJOR 10