Skip to content

v.diagserver #

-os cross compiles this file into vc/v.c for every Unix, and only $if guards reach the C preprocessor there: prctl and the memfd_create syscall are Linux's.

fn quick_sum_digest #

fn quick_sum_digest(sum u64, size int) string

quick_sum_digest writes a token.quick_sum of a source of size bytes as a digest of the inputs a child keeps (see keep_inputs), where it stands for a SHA-256.

fn serve #

fn serve() Request

serve turns this compilation into a diagnostics server when the environment sets V_DIAGNOSTICS_SERVER, and returns an empty request at once otherwise. The work done so far - builtin, parsed - is kept for every request: each check line read on stdin is answered by a child created by fork(), which returns from here and finishes the compilation exactly as a one-shot run of the same command line would, printing its diagnostics and exiting with its exit code. Once that child is gone the server prints v-diagnostics-server: end <code> <token> on a line of its own, where the token is whatever followed check on the request line: a source line quoted in a diagnostic cannot then pass for the end of the answer. It stops when stdin closes or a quit line arrives.

A query <token> <file>:<line>:<code><column> line asks a question of the mini-VLS protocol instead, as -line-info does: its child returns the question, and answers it in place of the diagnostics. The token comes first, as the path of the file may hold spaces. The child that answered a query stays, with the program it checked: the next query goes to it while every file it read holds the same bytes, no file was added next to one of them or removed, and each import resolves to the directory it was read from, and to a new child that checks the program otherwise.

With V_DIAGNOSTICS_SHARED set, checks and queries share their children: the child of a check stays too, and the next check or query goes to the child that checked the program, while its files hold (see diagnose_in_grandchild). A client that asks both of each version of a program checks it once.

Such a check checks again only the bodies of the functions whose code changed since the last check of the program, and takes what the others reported from what that check left in a file of the server (see types.start_incremental_check). The child checks the bodies it left out while it waits for a question, as a question reads their types. It leaves out no body unless those it would leave out hold V_DIAGNOSTICS_INCREMENTAL_MIN_NODES nodes (2048 by default). V_DIAGNOSTICS_INCREMENTAL=0 checks every body; with V_DIAGNOSTICS_INCREMENTAL_VERIFY set, the child checks them before it tells the server it answered, and traces each body that reports otherwise than what was put back for it (see V_DIAGNOSTICS_TRACE).

The children of the last versions of the program stay, up to V_DIAGNOSTICS_WARM_CHILDREN (3 by default): a child whose files changed waits for them to hold what it read again, as when an edit is undone, and answers then at once. Each request goes to the child that holds the version on disk, which each of them looks for at once, and to a new child when none does.

A request carries nothing else: the command line fixes the input, and with it every setting the driver derived from the input before this point. Between requests the client changes the files on disk, and it only shows the diagnostics of its own files: see V_CHECK_SELECTED_FILES_ONLY.

struct Request #

struct Request {
pub:
	question string
	// from_server tells a child of the server from a one-shot compilation.
	from_server bool
mut:
	questions LineReader // the next questions from the server, and its `go`
	status_fd int = -1 // where the child tells the server what it does
	inputs    Inputs // the files the check read
	buffer    []u8   // where the child reads them again
	current   bool   // they held what the check read when the child looked
	// digests are what the check read in each file, which the child reads
	// again once its first answer is out (see keep_inputs).
	digests  map[string]string
	base_kb  i64    // the child's resident memory after its first answer
	work_dir string // the input directory, which the child enters by name
	// shares_children is set in a server whose checks and questions share their
	// children.
	shares_children bool
	diagnostics     Diagnostics
	// token is the one of the check the child answers, which ends its partial
	// answer (see print_diagnostics), when the client takes partial answers
	// (V_DIAGNOSTICS_PARTIAL).
	token    string
	partials bool
	// partial prints what the check found before the grandchild went on, and
	// returns the exit code it gives.
	partial fn () int = unsafe { nil }
	// record_fd holds what the last check that recorded one left for the next
	// check of the program (see keep_incremental_record).
	record_fd int = -1
	// idle does part of the work the child can do while it waits for a question,
	// and reports whether some remains (see keep_busy_with).
	idle fn () bool = unsafe { nil }
}

Request is what a child of the diagnostics server was made for: answering question, the questions of a query line, or checking the program when it is empty. A child that answered can answer the next questions too, while the files it read hold what they held (see next_question). In a server that shares its children, it answers the next checks too.

fn (Request) answers_again #

fn (r &Request) answers_again() bool

answers_again reports whether this child can answer more questions after its first ones: the server gave it a way to receive them.

fn (Request) shares_checks #

fn (r &Request) shares_checks() bool

shares_checks reports whether this child answers the checks of its program too, besides its questions.

fn (Request) print_partial_with #

fn (mut r Request) print_partial_with(print fn () int)

print_partial_with sets what prints the diagnostics the check found before the grandchild went on, and returns the exit code they give: a check whose grandchild takes long sends them first (see print_diagnostics).

fn (Request) asks_for_diagnostics #

fn (r &Request) asks_for_diagnostics(question string) bool

asks_for_diagnostics reports whether question, which next_question returned, asks for the diagnostics of the program (see print_diagnostics).

fn (Request) diagnose_in_grandchild #

fn (mut r Request) diagnose_in_grandchild() bool

diagnose_in_grandchild makes a grandchild that goes on with the check, and prints the diagnostics of the program into a file, as a one-shot check prints them: the rest of the check rewrites the tree that the child answers its questions from. It returns true in the grandchild, which ends with the check. In the child it returns false, unless no grandchild can be made and the child was made for a check: it then goes on with the check itself, answers nothing more, and ends with it.

fn (Request) keep_busy_with #

fn (mut r Request) keep_busy_with(step fn () bool)

keep_busy_with has the child call step while it waits for its next question, until step returns false or a question comes: the work its next answers need, done before they are asked.

fn (Request) print_diagnostics #

fn (mut r Request) print_diagnostics() int

print_diagnostics prints what the grandchild printed for the program, once it ended, and returns its exit code, that of a one-shot check. For a client that takes partial answers (V_DIAGNOSTICS_PARTIAL), a grandchild that takes longer than partial_answer_ms has the child print the diagnostics it found before first (see print_partial_with), and end them on a line of their own, v-diagnostics-server: partial <code> <token>: the client can show the errors of the program while the rest of the check runs, the instances of its generic functions above all.

fn (Request) incremental_record #

fn (r &Request) incremental_record() string

incremental_record returns what the last check of the program that recorded something left for the next one (see keep_incremental_record), or ''.

fn (Request) keep_incremental_record #

fn (r &Request) keep_incremental_record(text string)

keep_incremental_record keeps text for the next check of the program, in place of what an earlier check kept: a check that checks again only the function bodies that changed reads it (see types.start_incremental_check).

fn (Request) keep_inputs #

fn (mut r Request) keep_inputs(digests map[string]string, imports_hold fn () bool)

keep_inputs takes the files the check read, by absolute path, with the SHA-256 of what it read in each, in hexadecimal, or its quick_sum_digest: the child answers a next question only while they hold it, no file was added next to one of them or removed, and imports_hold finds each import resolving to the directory it was read from. It notes the names in their directories already. A quick sum is what the check read: the file is read again only for the next question. A file known by its SHA-256 is read again once the first answer is out, as the client does not wait for that.

fn (Request) next_question #

fn (mut r Request) next_question(code int) ?string

next_question tells the server that the answer is complete, with the exit code a one-shot run would have, and returns the next question it sends, or none once the server wants no more from this child. The memory a child allocates for its answers is never freed: after it has grown by V_DIAGNOSTICS_RETIRE_MB (1024 by default) since its first answer, it answers no more, and a new child checks the program for the next question.