Synthetic memory analysis
This runnable project is the executable companion to the memory guide series.
It has two parts. MemoryTutorialExamples.cs contains the nine snippets that the guides include verbatim, each
ending in assertions that check the numbers the guides quote. Program.cs then runs a longer scenario that
combines the same ideas into one small analysis. Nothing in it needs a real capture, profile, or live process:
the program constructs its own metadata and byte image, so you can read every input byte in the source.
Use using CStructSharp.Memory; for sources, schemas, and sessions, and using CStructSharp.Memory.Metadata;
for the importers.
Run it
From the repository root:
dotnet run --project docs/examples/memory-analysis -c Release -f net10.0
dotnet run --project docs/examples/memory-analysis -c Release -f net8.0
Success starts with Nine memory guide examples passed. and continues with the scenario's output. Any failed
assertion exits the program with an error. The documentation validation script runs both commands, so the guides
cannot drift from the implementation without a failing build.
The nine guide snippets
Each snippet is a #region that a guide includes with [!code-csharp[...]], so the code you see in an article
is exactly the code being tested. Require is a one-line assertion helper that throws with the failing
description.
| Snippet | Guide | What it checks |
|---|---|---|
| A four-byte field split across two mappings, plus a finite stream view | Address spaces | Logical address, two backing fragments, stream position zero |
| Explicit padding and a signed three-bit update | Schemas | Sign extension to -1, neighbor bits preserved on write |
| A synthetic ISF type with a member at offset four | Schemas | Imported offsets are used, not inferred |
| A synthetic BTF table with explicit member placement | Schemas | Bit offset 32 becomes byte offset 4 |
| A pointer encoded as a displacement from its containing record | Traversal | Stored bits stay 16 while the target resolves to 24 |
| A sentinel list and embedded-member address calculation | Traversal | Two data nodes, sentinel excluded, ContainingRecord arithmetic |
| A two-fragment overlay patch and physical-image export | Updates | Preview is read-only, original unchanged, export keeps gaps |
| Creation of a union from a chosen member or raw bytes | Updates | Explicit interpretation, exact raw storage |
| Cache accounting, generation invalidation, missing mappings, cancellation | Reliability | Zero backing bytes on a hit, Unmapped versus OperationCanceledException |
The combined scenario
Program.cs models a tiny operating-system kernel keeping a circular list of tasks. Reading it top to bottom
follows the order of the guides.
- Metadata and image. An ISF document describes a 16-byte
taskwith apidat offset 0 and anextpointer at offset 8. A 40 KiBByteArrayMemorySourceplays the role of the capture file. - A kernel address space. A
MappedMemorySourceplaces two 4096-byte pages at0xffff800000000000and the page after it, backed by image offsets0x2000and0x9000. The pages are adjacent to the kernel and far apart in the file, exactly the situation the first guide describes. - Records that cross a page.
WriteTaskserializes each record and writes it withMemoryPatch.Create. The task with PID 42 starts atKernel + 4094, so itspidstraddles the page boundary. - A sentinel walk with one budget.
MemoryWalker.SentinelListfollows the list head through PIDs 42 and 99 and back to the head. The shared context reports 32 physical bytes and 20 requests for the walk and two selected PID reads: three pointer reads and two PID reads, with the cross-page PID costing two mapping lookups. - The same number, two spaces. A second
MappedMemorySourcefor "process 7" maps the same numeric address to a different image offset. Readingpidthrough each source gives 99 and 0 respectively, showing that an address only means something together with its source. - A tagged tree. The child callback strips the low tag bit from an entry before returning a region, and the
walker reports
Completewith two nodes. - Corrupt and missing input. A callback that always returns the first node produces
RepeatedNodeafter one node; removing the second page producesUnavailablewith anUnmappedfailure. Neither invents a sentinel or a zero pointer. - An offline patch. An
OverlayMemorySourceover the image and a second mapping table on top of it let the program change PID 42 to 123. The patch has two fragments because the field crosses the page boundary, the read after commit returns 123, and the original image is byte-for-byte unchanged. - A cache over the copy. A cold read requests four backing bytes; the warm read requests zero.
Read the overview for the coordinate systems, metadata subset, ownership rules, and update failure behavior that the scenario relies on.