- Zig 100%
|
|
||
|---|---|---|
| screenshots | fixed sentinel terminated segments not including the final sentinel element | |
| src | updated to 0.17.0-dev.304+9787df942 | |
| .gitignore | All tests pass | |
| build.zig | updated to 0.17.0-dev.304+9787df942 | |
| build.zig.zon | removed core_utils dependency | |
| LICENSE | removed core_utils dependency | |
| README.md | updated to 0.17.0-dev.304+9787df942 | |
Lens
Zig debugging library with a focus on memory forensics, data inspection and developer ergonomics.
Currently Linux only
Data formatters
Formatters give you instant, frictionless visibility into the state of your algorithms and data structures. They are designed to completely bypass the setup overhead of a traditional debugger, giving you powerful structural insights with a single drop-in function call.
constlens=@import("lens");constdebug=lens.debug;{// The one-parameter functions use the default config and thread-safe allocatorsdebug.inspectMemory(self);debug.prettyPrint(self)debug.prettyPrint(.{// any type of input is accepted.i=i,.current_state=self,.some_var=some_var,});}All input is assumed to be potentially corrupt and strictly validates memory before reading. The goal is to never crash, and to succesfully format all types of data via comptime reflection.
Pretty printing
Formats any Zig structure into a human readable graphic. Some highlights include:
- Dynamically decide whether to use newline or comma separation of elements
- Iterator detection
- Terminal colorscheme respecting ANSI highlighting
- String heuristics, utf8 and unprintable character escaping
- Fault tagging; notifies when a memory read was not performed, e.g. would cause a segfault, tag is broken...
- Highly configurable; default is to highly filter noise
The goal is so that you never have to write another custom formatter for a datastructure. Instead let the default behaviour do its work, or define a custom iterator for it if its a highly optimized object. See pretty_print.zig for its configuration.
iterators
See above with iterators off
state machine
code
Memory analysis
Structurally visualizes all memory clusters found in the input. The top cluster, e.g. the value passed-by-copy to the function, is only used for memory-object collection purposes. Some key features:
- Explains the explicit reason why a pointer is broken
- Notifies on semantic problems: underlying memory is zig-undefined, tag is broken, misalignment...
- Perfectly segmentizes composite types: optionals, error unions, tagged unions, structs. This segmentation perfectly mirrors how the Zig compiler segmentizes such types. Homogeneous sequences are not segmented to avoid spam (slices, arrays)
- Visualizes memory aliasing and adjacency
- Prints relevant OS information for the found memory segments
See mem_inspect.zig for configuration.
Example with two game entities that target each other
constGameEntity=struct{id:u64,title:?[]constu8,stats:?PlayerStats,magic_state:anyerror!void,inventory:[3]InventoryItem,grid_pos:[2][2]?u8,target:?*constSelf,faction_id:u8align(32),constSelf=@This();};code
pretty printed input
mem inspect output
Zig EnumMap
Broken memory examples
code
memory inspect output
memory inspect output
- The memory validity tags are assigned in a strict precedence order. See analysis.zig for more info on the tags.
- The top level names in the graphic signify memory regions that were found through pointer-objects.
- The tree graphic underneath it is its segmentation.
- The cyan-colored regions are regions that contain aliasing.
- A mode of
r--p/rw-signifies thatproc/mapsreportedr--pwhile ELF reportedrw-
Sentinel scanning
Sentinels are searched across contiguous memory regions
Known crash conditions
Multithreaded race conditions
The formatters parse proc/maps for determining truth on whether a pointer is pointing to a legal region. This is subject to a race condition where another thread frees memory the formatting thread believes to be valid which then causes a segfault
Testing
Deep equality
trylens.testing.expectDeeplyEqual(expected,actual);Similar to Zig's std.testing.expectEqualDeep, it's a function for comparing two instances according to the structural semantics defined in src/mem/deeply_equal/equal.zig.
The main purpose is to provide a more ergonomic way to compare sequences with each other.
This should not be used as a general comparison algorithm. Usually the shape of the data is as important as the data itself. This function purely ignores the shape and strict Zig compiler type safety of the data.
Asymmetric unwrapping
All unwrappable types are unwrapped asymmetrically. This includes optionals, unions, error unions, one pointers.
constexpected=5;constmaybe_union:??MyUnion=MyUnion{.value=5};varptr:??*const??MyUnion=&maybe_union;trylens.testing.expectDeeplyEqual(expected,&&&ptr);// passesexpected could also be an arbitrarily wrapped type
Sequences
All of the following are interpreted as a Sequence polymorphism: Tuples, iterators, slices, arrays, vectors and sentinel terminated many pointers.
This makes it extremely ergonomic to test iterators. Consider some complicated algorithm that returns an iterator with a very complicated return type
constResult=union(enum){obj:SomeObject,value:usize,name:[]constu8,maybe_thing:?Something,};constError=error{a,b,c}||Allocator.Error;constIterationResult=Error!Result;In order to perform a structural expectation check, all you do is:
varit=my_complicated_algorithm.iterator();trylens.testing.expectDeeplyEqual(.{5,2,3,error.a,"mark",null,// @typeOf(null) matches any optional with the null tagerror.c,// error literals not belonging to the same set worksnull,"jack",2,},&it);// passesThe structure of the data is completely ignored. This means that if you are comparing a type such as:
constIterationResult=Error!?Result;A null Result and a null maybe_thing field will be interpreted as equal. Potentially causing false positives
Usage
Soft dependency
The library is meant to be used during development and then deleted for release
See render_target.zig for configuration fields
// in build.zigconstlens_dep=b.dependency("lens",.{.target=target,.optimize=optimize,.render_ansi=false,// disable color});exe.root_module.addImport("lens",lens_dep.module("lens"));// in build.zig.zon.dependencies=.{.lens=.{.url="https://codeberg.org/jetill/lens/archive/v0.1.4.tar.gz",},},It can also be enabled as a testing-only soft dependency or via a dev flag
// -Ddev=true constis_dev=b.option(bool,"dev","Enable dev dependencies (lens) inside the core module")orelsefalse;// Always injected to tests// Only injected to main if -Ddev=trueif(b.lazyDependency("lens",.{.target=target,.optimize=optimize,}))|lens_dep|{constlens_mod=lens_dep.module("lens");all_tests.root_module.addImport("lens",lens_mod);if(is_dev){pzre_mod.addImport("lens",lens_mod);}}Debug no-op
The debug interface can also be turned into no-op automatically in non-debug mode.
constlens_dep=b.dependency("lens",.{.target=target,.optimize=optimize,.debug_only=true,});