Description
There exist a very potent diagnostic technique, long and very successfully used in bigger C language projects (most prominently in Linux Kernel). Unfortunately, managed memory languages seldom implement it even though managed memory makes it rather fail safe, as compared to something like C (it is doable in JVM, of course, but then, almost anything is doable in JVM :-).
In Go, it may look the following:
package foo
func Bar() {
myLabel: if {
// seldom used diagnostic code
}
// oft used code
}
Here we have an if
statement without a condition block. By default, it's simply a jump to the (possibly implicit) "else" code block. However, we may add some sort of API (based on reflect
for example) which can be used to "steer" that if
:
reflect.ValueOf(foo.Bar).ConstIfByLabel("myLabel").Set(true)
This will make the if
statement at myLabel
to enter the "then" code block. Setting the controller back to "false" will restore the initial state ("then" code block will be skipped). The label can also be made implicit, auto generated out of code location, thus saving some programmers effort.
The feature is pretty straightforward to implement in the compiler. Indeed, 2 methods can be readily employed:
- "Zero idle cost" method can involve code modification (x86_64 is pretty friendly in this regards). Compiler can set aside generated opcodes for "goto myLabel_then" and "goto myLabel_else" which will be injected by the steering code in
reflect
directly into the "myLabel" location (invoking the necessary mprotect and icache magic as necessary, steering performance being of not much concern). - "Safe" method whereupon compiler automatically creates a unique global scope bool flag and emits a simple conditional branch predicated by that flag. The bool flags are then registered in the special compile time table workable by the steering code in
reflect
(possibly in dedicated linker section).
It shall be noted, that Linux kernel employs both methods for its various features (the "safe" bool flag method uses a macro and a dedicated linker section).
Why is this useful and what are the alternatives?
When working with highly loaded or otherwise long running network servers we often want to extract some data for diagnostics. Unfortunately, side channel data extraction always carries a rather high performance cost. Even a single extra call to a logging system can have a substantial cost if its parameters are chosen in unfortunate way.
Thus, server developers and operators can really benefit from some mechanism to selectively enable diagnostic code blocks on one by one basis in runtime; the benefit will be even greater if the mechanism for doing so is consistent across the wider Go ecosystem, that is, happens to be part of the language.
Right now, Go doesn't offer any sort of viable alternative to address the problem of selective diagnostics in runtime.
Instead, we are left at the mercy of coarse grained, library level, non standardized global debug switches, which invariably emit either too little or too much data and never play nice with the application code (examples being Go runtime itself, libraries like GRPC and many other). The switches themselves require too much boilerplate and programmer's discipline to use on the library level, may only affect things on start-up and can be rather troublesome in general, both for the implementer and for the library user.
Providing an efficient runtime code steering facility in the core language will substantially improve observability and maintainability of a large number of Go programs.