
In this transcript we'll navigate to an anonymous mapping structures
in a process' address space, as depicted in Chapter 9 in Fig. 9.11.

Suggestion: repeat this walkthrough for a mmaped _file_ (rather than
an anonymous mapping). Do it also for a _shared_ mapping.

As a test program, we'll use mmap.c:

-------------------------- begin mmap.c -------------------------
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>   

int main()
{
  void *addr = mmap(NULL, 4096*3, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
  if(addr==MAP_FAILED){
    perror("mmap failed: ");
    return -1;
  }

  printf("%p\n", addr);

  *(unsigned long*)addr = 0xdeadbeef;           // initially commented out
  *(unsigned long*)(addr+4097) = 0xdeadbeef;    // initially commented out
  *(unsigned long*)(addr+4097*2) = 0xdeadbeef;  // initially commented out

  void *addr1 = mmap(NULL, 4096*3, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
  if(addr1==MAP_FAILED){
    perror("mmap failed: ");
    return -1;
  }

  printf("%p\n", addr1);

  sleep(10); // so we can catch and stop it

  return 0;
}
-------------------------- end mmap.c -------------------------

$ gcc -Wall -o mmap mmap.c
  --this makes a 32-bit version (observe 32-bit addresses in the process image);
    To build a 64-bit executable, use "gcc -m64 -o mmap64 mmap.c"

Suggestion: examine the memory layout of the 64-bit executable.

Running the executable and stopping it to examine in mdb:
$ ./mmap
fee10000
fee00000
^C
$ ./mmap & kill -STOP `pgrep mmap`
fee10000
fee00000
[2] 8757

[2]+  Stopped                 ./mmap

(Notice that mmap gives us the same addresses from run to run, and 
allocates a lot more than the requested 12K---i.e., 64K per call. 
See "man mmap" for why and how the exact rounding up is done).

Now the executable is stopped, let's examine its address space in mdb: 

root@openindiana:/home/sergey# mdb -k
Loading modules: [ unix genunix specfs dtrace mac cpu.generic uppc pcplusmp scsi_vhci zfs sata sd ip hook neti sockfs arp usba stmf stmf_sbd fctl md lofs random idm sppp ipc ptm fcp cpc fcip crypto nsmb smbsrv nfs ufs logindmux ]
> ::ps
S    PID   PPID   PGID    SID    UID      FLAGS             ADDR NAME
R      0      0      0      0      0 0x00000001 fffffffffbc2e330 sched
R      3      0      0      0      0 0x00020001 ffffff014cfe3028 fsflush
R      2      0      0      0      0 0x00020001 ffffff014cfe4020 pageout
R      1      0      0      0      0 0x4a004000 ffffff014cfe7018 init
R   8132      1   8132   1725      0 0x4a004000 ffffff015bd43078 xeyes
R   8062      1   8061   8061      0 0x42000000 ffffff015be990b0 sshd
R   7994      1   7994   7994      0 0x52010000 ffffff01531f2078 sendmail
R   7987      1   7987   7987     25 0x52010000 ffffff015bd40088 sendmail
<skip>

> ::pgrep mmap                        
S    PID   PPID   PGID    SID    UID      FLAGS             ADDR NAME
R   8757   1725   8757   1725    101 0x4a004000 ffffff0158b57010 mmap
> ffffff0158b57010::print -t proc_t
proc_t {
    struct vnode *p_exec = 0xffffff017fb30980
    struct as *p_as = 0xffffff0157a3c4b0
    struct plock *p_lockp = 0xffffff014ab87580
    kmutex_t p_crlock = {
        void *[1] _opaque = [ 0 ]
    }
    struct cred *p_cred = 0xffffff0155ebe530
<skip>

Let's see if it's really the file we created the process from :)

> ffffff0158b57010::print -t proc_t p_exec 
struct vnode *p_exec = 0xffffff017fb30980
> ffffff0158b57010::print -t proc_t p_exec | ::print -t vnode_t
vnode_t {
    kmutex_t v_lock = {
        void *[1] _opaque = [ 0 ]
    }
    uint_t v_flag = 0x1000
    uint_t v_count = 0x1
    void *v_data = 0xffffff014e431e88
<skip some>
    char *v_path = 0xffffff017e765d78 "/home/sergey/mmap/mmap"

Indeed it is. Using a more succinct command by taking advantage of 
MDB's pipes:

> ffffff0158b57010::print -t proc_t p_exec | ::print -t vnode_t v_path
char *v_path = 0xffffff017e765d78 "/home/sergey/mmap/mmap"

OK then.

> ::pgrep mmap
S    PID   PPID   PGID    SID    UID      FLAGS             ADDR NAME
R   8757   1725   8757   1725    101 0x4a004000 ffffff0158b57010 mmap

> ffffff0158b57010::print proc_t p_as
p_as = 0xffffff0157a3c4b0

> 0xffffff0157a3c4b0::print -t 'struct as'
struct as {
    kmutex_t a_contents = {
        void *[1] _opaque = [ 0 ]
    }
    uchar_t a_flags = 0
    uchar_t a_vbits = 0
    kcondvar_t a_cv = {
        ushort_t _opaque = 0
    }
    struct hat *a_hat = 0xffffff01567fe4f0  <--- this wraps the process' actual page table
    struct hrmstat *a_hrm = 0
    caddr_t a_userlimit = 0xfefff000
    struct seg *a_seglast = 0xffffff01594b1060
    krwlock_t a_lock = {
        void *[1] _opaque = [ 0 ]
    }
    size_t a_size = 0x188000
    struct seg *a_lastgap = 0xffffff015b8b8570
    struct seg *a_lastgaphl = 0
    avl_tree_t a_segtree = {    <--- this is the AVL tree of segments. We could walk it manually.
        struct avl_node *avl_root = 0xffffff0155f69510  
        int (*)() avl_compar = as_segcompar
        size_t avl_offset = 0x20
        ulong_t avl_numnodes = 0x11
        size_t avl_size = 0x60        
    }                                 
<skip>

Instead of walking the AVL tree of segments manually, we can use the pmap DCMD.
Note our mmap-ed areas, both 12K (but 64K apart):

Also notice that their resident size is 0K. This is before we uncommented the 
actual memory writes of 0xdeadbeef.

> ffffff0158b57010::pmap         
             SEG             BASE     SIZE      RES PATH
ffffff01807f39d0 0000000008046000       8k       8k [ anon ]
ffffff0157a2abf0 0000000008050000       4k       4k /export/home/sergey/mmap/mma
ffffff015946a590 0000000008060000       8k       8k /export/home/sergey/mmap/mma
ffffff01594b1060 00000000fee00000      12k       0k [ anon ]   <--- 2nd allocated mmap-ed area
ffffff015b8b8570 00000000fee10000      12k       0k [ anon ]   <--- 1st allocated mmap-ed area 
ffffff0157a47280 00000000fee20000      24k      12k [ anon ]
ffffff015bb89c00 00000000fee30000       4k       4k [ anon ]
ffffff0151283c58 00000000fee40000    1216k     940k /usr/lib/libc/libc_hwcap1.so
ffffff0151283b98 00000000fef70000      36k      36k /usr/lib/libc/libc_hwcap1.so
ffffff0159476f48 00000000fef79000       8k       8k [ anon ]
ffffff0155e726f8 00000000fef80000       4k       4k [ anon ]
ffffff01594ecec0 00000000fef90000       4k       4k [ anon ]
ffffff0155f694f0 00000000fefa0000       4k       4k [ anon ]
ffffff015c2fadc0 00000000fefb0000       4k       4k [ anon ]
ffffff01594c6070 00000000fefb7000     208k     208k /lib/ld.so.1
ffffff015c3259b8 00000000feffb000       8k       8k /lib/ld.so.1
ffffff01594c6910 00000000feffd000       4k       4k [ anon ]

Another handy DCMD to summarize a segment struct. Note that it gives
the starting address and the extent of a segment, as well as its type,
i.e., the ops it supports:

> ffffff0157a2abf0::seg
             SEG             BASE             SIZE             DATA OPS
ffffff0157a2abf0         08050000             1000                0 segvn_ops

This, by the way, is the practical definition of what a TYPE is in
Programming Languages: some structured data that admits a specific set
of operations (in our case, the segvn_ops set).

Viewing the same segment

> ffffff0157a2abf0::print -t seg_t
seg_t {
    caddr_t s_base = 0x8050000 
    size_t s_size = 0x1000
    uint_t s_szc = 0
    uint_t s_flags = 0
    struct as *s_as = 0xffffff0157a3c4b0
    avl_node_t s_tree = {
        struct avl_node *[2] avl_child = [ 0, 0xffffff015b8b8530 ]
        uintptr_t avl_pcb = 0xffffff01807f39f2
    }
    struct seg_ops *s_ops = segvn_ops
<skip>

[At this point I accidentally killed my process. Accordingly,
 my next attempt to interpret its memory gave me inconsistent 
 results---instead of my process descriptor I got some pages
 reclaimed by the kernel, which made no sense for the user process:]

> ffffff0158b57010::pmap
             SEG             BASE     SIZE      RES PATH
fffffffffbc6c960 fffffe0000000000 2125824k        ? [ &segkpm_ops ]
fffffffffbc340e0 ffffff0000000000   65536k        ? [ &segkmem_ops ]
fffffffffbc314c0 ffffff0004000000 2097152k        ? [ &segkp_ops ]
fffffffffbc351d0 ffffff0084000000 2117632k        ? [ &segkmem_ops ]
fffffffffbc34010 ffffff0145400000   65536k        ? [ &segmap_ops ]
fffffffffbc31530 ffffff0149400000 1067298816k        ? [ &segkmem_ops ]
fffffffffbc7e9b0 ffffffffc0000000  974828k        ? [ &segkmem_ops ]
fffffffffbc7d490 fffffffffb800000    5440k        ? [ &segkmem_ops ]
fffffffffbc7d620 ffffffffff800000    4096k        ? [ &segkmem_ops ]

Indeed, my process was no longer alive:

> ::pgrep mmap

<nothing>

So I restarted it again. Working up to the new descriptor and struct as:

> ::pgrep mmap
S    PID   PPID   PGID    SID    UID      FLAGS             ADDR NAME
R   8775   1725   8775   1725    101 0x4a004000 ffffff0158b57010 mmap

> ffffff0158b57010::pmap
             SEG             BASE     SIZE      RES PATH
ffffff015b8bd508 0000000008046000       8k       8k [ anon ]
ffffff0157a47280 0000000008050000       4k       4k /export/home/sergey/mmap/mma
ffffff0159476f48 0000000008060000       8k       8k /export/home/sergey/mmap/mma
ffffff015c2fadc0 00000000fee00000      12k       0k [ anon ]
ffffff01594ecec0 00000000fee10000      12k       0k [ anon ]
ffffff015b8b8570 00000000fee20000      24k      12k [ anon ]
ffffff015c320e40 00000000fee30000       4k       4k [ anon ]
ffffff015bbc1848 00000000fee40000    1216k     940k /usr/lib/libc/libc_hwcap1.so
ffffff015326aba0 00000000fef70000      36k      36k /usr/lib/libc/libc_hwcap1.so
ffffff015bb89c00 00000000fef79000       8k       8k [ anon ]
ffffff0151283c58 00000000fef80000       4k       4k [ anon ]
ffffff01594c6070 00000000fef90000       4k       4k [ anon ]
ffffff01594c6910 00000000fefa0000       4k       4k [ anon ]
ffffff014d8b5b80 00000000fefb0000       4k       4k [ anon ]
ffffff0151283b98 00000000fefb7000     208k     208k /lib/ld.so.1
ffffff015b8b82d0 00000000feffb000       8k       8k /lib/ld.so.1
ffffff0159187878 00000000feffd000       4k       4k [ anon ]

Having a look at a file-mapped segment of my executable:

> ffffff0159476f48::print -ta seg_t
ffffff0159476f48 seg_t {
    ffffff0159476f48 caddr_t s_base = 0x8060000
    ffffff0159476f50 size_t s_size = 0x2000
    ffffff0159476f58 uint_t s_szc = 0
    ffffff0159476f5c uint_t s_flags = 0
    ffffff0159476f60 struct as *s_as = 0xffffff0157a3c4b0
    ffffff0159476f68 avl_node_t s_tree = {
        ffffff0159476f68 struct avl_node *[2] avl_child = [ 0xffffff015b8bd528, 
0xffffff01594ecee0 ]
        ffffff0159476f78 uintptr_t avl_pcb = 0xffffff015c320e61
    }
    ffffff0159476f80 struct seg_ops *s_ops = segvn_ops   <--- actual type of this segment, segvn. 
    ffffff0159476f88 void *s_data = 0xffffff0159477688   <--- void*, actually segvn_data 
<skip>

The object-oriented design of the segment system (discussed in 9.4.4, 9.5, and 9.5.1 
of the textbook) comes down to the particular set of "ops" functions recasting
the void* s_data pointer to the actual data type they are written to work with.
For segvn_ops functions, this is struct segvn_data:

> 0xffffff0159477688::print -t 'struct segvn_data'
struct segvn_data {
    krwlock_t lock = {
        void *[1] _opaque = [ 0 ]
    }
    kmutex_t segfree_syncmtx = {
        void *[1] _opaque = [ 0 ]
    }
    uchar_t pageprot = 0
    uchar_t prot = 0xf
    uchar_t maxprot = 0xf
    uchar_t type = 0x2
    u_offset_t offset = 0
    struct vnode *vp = 0xffffff017e8f4480   <--- our vnode
<skip>

> 0xffffff0159477688::print -t 'struct segvn_data' vp
struct vnode *vp = 0xffffff017e8f4480

> 0xffffff0159477688::print -t 'struct segvn_data' vp | ::print vnode_t v_path
v_path = 0xffffff015b8fc7f0 "/export/home/sergey/mmap/mmap"

Makes sense, it's our executable file.

Suggestion: follow through to the vnode's list of page_t per-page structs
to see how the file is mapped into the memory.

Back to our anonymous segments.

> ffffff0158b57010::pmap
             SEG             BASE     SIZE      RES PATH
ffffff015b8bd508 0000000008046000       8k       8k [ anon ]
ffffff0157a47280 0000000008050000       4k       4k /export/home/sergey/mmap/mma
ffffff0159476f48 0000000008060000       8k       8k /export/home/sergey/mmap/mma
ffffff015c2fadc0 00000000fee00000      12k       0k [ anon ]
ffffff01594ecec0 00000000fee10000      12k       0k [ anon ]
ffffff015b8b8570 00000000fee20000      24k      12k [ anon ]
ffffff015c320e40 00000000fee30000       4k       4k [ anon ]
ffffff015bbc1848 00000000fee40000    1216k     940k /usr/lib/libc/libc_hwcap1.so
ffffff015326aba0 00000000fef70000      36k      36k /usr/lib/libc/libc_hwcap1.so
ffffff015bb89c00 00000000fef79000       8k       8k [ anon ]
ffffff0151283c58 00000000fef80000       4k       4k [ anon ]
ffffff01594c6070 00000000fef90000       4k       4k [ anon ]
ffffff01594c6910 00000000fefa0000       4k       4k [ anon ]
ffffff014d8b5b80 00000000fefb0000       4k       4k [ anon ]
ffffff0151283b98 00000000fefb7000     208k     208k /lib/ld.so.1
ffffff015b8b82d0 00000000feffb000       8k       8k /lib/ld.so.1
ffffff0159187878 00000000feffd000       4k       4k [ anon ]

This is the first one of them---but before any writes to the actual
mmaped page. Notice that the s_data has been created:

> ffffff01594ecec0::print seg_t
{
    s_base = 0xfee10000
    s_size = 0x3000
    s_szc = 0
    s_flags = 0
    s_as = 0xffffff0157a3c4b0
    s_tree = {
        avl_child = [ 0xffffff015c2fade0, 0xffffff015b8b8590 ]
        avl_pcb = 0xffffff0159476f6d
    }
    s_ops = segvn_ops
    s_data = 0xffffff01594edd80
    s_pmtx = {
        _opaque = [ 0 ]
    }
    s_phead = {
        p_lnext = 0xffffff01594ecf10
        p_lprev = 0xffffff01594ecf10
    }
}

...but not the anon_map: 

> 0xffffff01594edd80::print -t 'struct segvn_data'
struct segvn_data {
    krwlock_t lock = {
        void *[1] _opaque = [ 0 ]
    }
    kmutex_t segfree_syncmtx = {
        void *[1] _opaque = [ 0 ]
    }
    uchar_t pageprot = 0
    uchar_t prot = 0xb
    uchar_t maxprot = 0xf
    uchar_t type = 0x2
    u_offset_t offset = 0
    struct vnode *vp = 0       <---- no vnode; this is an anonymous mapping, not file-backed.
    ulong_t anon_index = 0
    struct anon_map *amp = 0   <---- The populating page fault did not happen yet.
    struct vpage *vpage = 0
    struct cred *cred = 0xffffff0155ebe530
    size_t swresv = 0x3000
    uchar_t advice = 0
    uchar_t pageadvice = 0
    ushort_t flags = 0x180
    spgcnt_t softlockcnt = 0
    lgrp_mem_policy_info_t policy_info = {
        int mem_policy = 0x1

Recompiling and restaring, to make sure the first page write happens.
Now 0xdeadbeef is written to the first of the three allocated pages.

> ::pgrep mmap
S    PID   PPID   PGID    SID    UID      FLAGS             ADDR NAME
R   8808   1725   8808   1725    101 0x4a004000 ffffff015bd5b040 mmap

Observe the resident set: it's now 4K for the first mmap-ed set. That's
only the first of the 3 requested pages---and the segment map reflects it. 

> ffffff015bd5b040::pmap  
             SEG             BASE     SIZE      RES PATH
ffffff015c320e40 0000000008046000       8k       8k [ anon ]
ffffff015326aba0 0000000008050000       4k       4k /export/home/sergey/mmap/mma
ffffff015bb89c00 0000000008060000       8k       8k /export/home/sergey/mmap/mma
ffffff0157a47280 00000000fee00000      12k       0k [ anon ]
ffffff0151283c58 00000000fee10000      12k       4k [ anon ]  <--- now the write happened!
ffffff01594c6070 00000000fee20000      24k      12k [ anon ]
ffffff01594c6910 00000000fee30000       4k       4k [ anon ]
ffffff014d8b5b80 00000000fee40000    1216k     940k /usr/lib/libc/libc_hwcap1.so
ffffff015c2fadc0 00000000fef70000      36k      36k /usr/lib/libc/libc_hwcap1.so
ffffff015b8b82d0 00000000fef79000       8k       8k [ anon ]
ffffff0159187878 00000000fef80000       4k       4k [ anon ]
ffffff0159476f48 00000000fef90000       4k       4k [ anon ]
ffffff015b8bd508 00000000fefa0000       4k       4k [ anon ]
ffffff015b8b8570 00000000fefb0000       4k       4k [ anon ]
ffffff0151283b98 00000000fefb7000     208k     208k /lib/ld.so.1
ffffff015bbc1848 00000000feffb000       8k       8k /lib/ld.so.1
ffffff01594ecec0 00000000feffd000       4k       4k [ anon ]

> ffffff0151283c58::print -t seg_t
seg_t {
    caddr_t s_base = 0xfee10000
    size_t s_size = 0x3000      <---- the segment has 3 pages (but only one is faulted in)
    uint_t s_szc = 0
    uint_t s_flags = 0
    struct as *s_as = 0xffffff0157a3c4b0
    avl_node_t s_tree = {
        struct avl_node *[2] avl_child = [ 0xffffff0157a472a0, 0xffffff01594c6090 ]
        uintptr_t avl_pcb = 0xffffff015bb89c25
    }
    struct seg_ops *s_ops = segvn_ops
    void *s_data = 0xffffff0151284680
<skip>

> ffffff0151283c58::print -t seg_t s_data | ::print -t 'struct segvn_data'
struct segvn_data {
    krwlock_t lock = {
        void *[1] _opaque = [ 0 ]
    }
    kmutex_t segfree_syncmtx = {
        void *[1] _opaque = [ 0 ]
    }
    uchar_t pageprot = 0
    uchar_t prot = 0xb
    uchar_t maxprot = 0xf
    uchar_t type = 0x2
    u_offset_t offset = 0
    struct vnode *vp = 0
    ulong_t anon_index = 0
    struct anon_map *amp = 0xffffff0157a65a88
<akip>

(and now we have the amp pointer finally created. It's the page
fault handler that did it.)

Observe that the second mmap-ed chunk is still empty in resident set,
i.e., it has not seen any page faults that would attach physical pages 
to the process:

> ffffff015bd5b040::pmap
             SEG             BASE     SIZE      RES PATH
ffffff015c320e40 0000000008046000       8k       8k [ anon ]
ffffff015326aba0 0000000008050000       4k       4k /export/home/sergey/mmap/mma
ffffff015bb89c00 0000000008060000       8k       8k /export/home/sergey/mmap/mma
ffffff0157a47280 00000000fee00000      12k       0k [ anon ]   <---- no pages attached yet
ffffff0151283c58 00000000fee10000      12k       4k [ anon ]   <---- one page faulted in/attached
<skip>

> ffffff0157a47280::print -t seg_t s_data | ::print -t 'struct segvn_data'
struct segvn_data {
    krwlock_t lock = {
        void *[1] _opaque = [ 0 ]
    }
    kmutex_t segfree_syncmtx = {
        void *[1] _opaque = [ 0 ]
    }
    uchar_t pageprot = 0
    uchar_t prot = 0xb
    uchar_t maxprot = 0xf
    uchar_t type = 0x2
    u_offset_t offset = 0
    struct vnode *vp = 0
    ulong_t anon_index = 0
    struct anon_map *amp = 0   <---- no fault yet, no anon_map needed.
<skip>

But back to the actually written segment:

> ffffff0151283c58::print -t seg_t s_data | ::print -t 'struct segvn_data'
struct segvn_data {
    krwlock_t lock = {
        void *[1] _opaque = [ 0 ]
    }
    kmutex_t segfree_syncmtx = {
        void *[1] _opaque = [ 0 ]
    }
    uchar_t pageprot = 0
    uchar_t prot = 0xb
    uchar_t maxprot = 0xf
    uchar_t type = 0x2
    u_offset_t offset = 0
    struct vnode *vp = 0
    ulong_t anon_index = 0
    struct anon_map *amp = 0xffffff0157a65a88   <--- we got anon_map
<skip>

> ffffff0151283c58::print -t seg_t s_data | ::print -t 'struct segvn_data' amp 
struct anon_map *amp = 0xffffff0157a65a88

> ffffff0151283c58::print -t seg_t s_data | ::print -t 'struct segvn_data' amp | ::print -t 'struct anon_map'
struct anon_map {
    krwlock_t a_rwlock = {
        void *[1] _opaque = [ 0 ]
    }
    size_t size = 0x3000
    struct anon_hdr *ahp = 0xffffff015270fe70
<skip>

> ffffff0151283c58::print -t seg_t s_data | ::print -t 'struct segvn_data' amp | ::print -t 'struct anon_map' ahp | ::print -t 'struct anon_hdr'
struct anon_hdr {
    kmutex_t serial_lock = {
        void *[1] _opaque = [ 0 ]
    }
    pgcnt_t size = 0x3
    void **array_chunk = 0xffffff01807eebd8
    int flags = 0
}

Only the first slot of the three has been instantiated yet, because only the
first page of the three pages mapped has been faulted in and attached yet:

> 0xffffff01807eebd8,3/K
0xffffff01807eebd8:             ffffff015c2ffc60          0               0               

The rest are still nulls. That is fine; let's see what the anon struct for the written
slot is: 

> ffffff015c2ffc60::print -t 'struct anon'
struct anon {
    struct vnode *an_vp = 0xffffff014d7fb640   <--- this is in SWAPFS
    struct vnode *an_pvp = 0
    anoff_t an_off = 0x1fffffe02b85f000        <---- ditto
    anoff_t an_poff = 0
    struct anon *an_hash = 0
    int an_refcnt = 0x1
}
> 0xffffff014d7fb640::print -t vnode_t
vnode_t {
    kmutex_t v_lock = {
        void *[1] _opaque = [ 0 ]
    }
    uint_t v_flag = 0x20040
    uint_t v_count = 0x1
    void *v_data = 0xbaddcafebaddcafe  <--- swapfs signature
    struct vfs *v_vfsp = 0
    struct stdata *v_stream = 0
    enum vtype v_type = 1 (VREG)
    dev_t v_rdev = 0xffffffffffffffff
    struct vfs *v_vfsmountedhere = 0
    struct vnodeops *v_op = 0xffffff014ac10d80    <---- see below
    struct page *v_pages = 0xffffff0001cef4b8     <---- associated physical page descriptor 
<skip>

> 0xffffff014ac10d80::print 'struct vnodeops'
{
    vnop_name = 0xfffffffffbbe8b88 "swapfs"       <---- swapfs ops signature
    vop_open = fs_nosys   <--- most file methods in swapfs return ENOSYS             
    vop_close = fs_nosys
    vop_read = fs_nosys
    vop_write = fs_nosys
    vop_ioctl = fs_nosys
    vop_setfl = fs_nosys
    vop_getattr = fs_nosys
    vop_setattr = fs_nosys
    vop_access = fs_nosys
    vop_lookup = fs_nosys
    vop_create = fs_nosys
    vop_remove = fs_nosys
    vop_link = fs_nosys
    vop_rename = fs_nosys
    vop_mkdir = fs_nosys
    vop_rmdir = fs_nosys
    vop_readdir = fs_nosys
    vop_symlink = fs_nosys
    vop_readlink = fs_nosys
    vop_fsync = fs_nosys
    vop_inactive = swap_inactive
    vop_fid = fs_nosys
    vop_rwlock = fs_rwlock            
    vop_rwunlock = fs_rwunlock        
    vop_seek = fs_nosys               
    vop_cmp = fs_cmp                  
    vop_frlock = fs_frlock            
    vop_space = fs_nosys              
    vop_realvp = fs_nosys             
    vop_getpage = swap_getpage        <--- getpage and putpage are primary methods for RAM swapping
    vop_putpage = swap_putpage        
    vop_map = fs_nosys_map            
    vop_addmap = fs_nosys_addmap      
    vop_delmap = fs_nosys             
    vop_poll = fs_nosys_poll          
    vop_dump = fs_nosys               

Fianlly, physical page descryptor:

> 0xffffff0001cef4b8::print -t page_t 
page_t {
    u_offset_t p_offset = 0x1fffffe02b85f000    <--- back references 
    struct vnode *p_vnode = 0xffffff014d7fb640  <--- back references 
    selock_t p_selock = 0
    uint_t p_vpmref = 0
    struct page *p_hash = 0xffffff0001fd6d20    <---- See Fig. 10.2 & section 10.2 
    struct page *p_vpnext = 0xffffff0001adef48
    struct page *p_vpprev = 0xffffff0003995798
    struct page *p_next = 0xffffff0001cef4b8
    struct page *p_prev = 0xffffff0001cef4b8
<skip>
    uchar_t p_embed = 0x1
    void *p_mapping = 0xffffff01808a0878
    pfn_t p_pagenum = 0x39f5b                  <--- Phys. page frame number 
    uint_t p_mlentry = 0x10           
<skip>

Finally, we get to verify that the physical page referenced by the page_t
struct we navigated to is indeed the physical page we wrote!

> 0x39f5b000\K                        
0x39f5b000:     deadbeef        
> 0x39f5b000,10\K
0x39f5b000:     deadbeef        0               0               0               
                0               0               0               0               
                0               0               0               0               
                0               0               0               0               

Can we list the entire page in increments of 8 bytes? (::formats K?)
I made a mistake and first specified 512 hex 8-byte entries :)

> 0x39f5b000,512\K
0x39f5b000:     deadbeef        0               0               0               
                0               0               0               0               
                0               0               0               0               
<skip>
                0               0               0               0               
                0               0               0               0               
                5f756e67006d7973 65636e6f6b6e696c 617665006365735f 6c69665f63655f6c 
                635f707369007365 745f747265766e6f 665f686500657079 6c756d5f656d6172 
<skip>

These ending lines looked like garbage, even though my mmap was supposed to
give me an entirely zero-filed (ZFOD) page. Then it dawned on me that the
MDB object count was hex, not decimal, and that 512 entries of 8 bytes were hex 200:

> 0x39f5b000,200\K
0x39f5b000:     deadbeef        0               0               0               
                0               0               0               0               
                0               0               0               0               
<skip>
                0               0               0               0               
                0               0               0               0               
                0               0               0               0               


Finally, recompiling to restore writes to all three pages of the first mmap-ed chunk:

> ::pgrep mmap
S    PID   PPID   PGID    SID    UID      FLAGS             ADDR NAME
R   8841   1725   8841   1725    101 0x4a004000 ffffff0158b57010 mmap
> ffffff0158b57010::pmap  
             SEG             BASE     SIZE      RES PATH
ffffff01803d6e68 0000000008046000       8k       8k [ anon ]
ffffff01807e51f8 0000000008050000       4k       4k /export/home/sergey/mmap/mma
ffffff01807de080 0000000008060000       8k       8k /export/home/sergey/mmap/mma
ffffff01803d65c8 00000000fee00000      12k       0k [ anon ]
ffffff01803d6628 00000000fee10000      12k      12k [ anon ]
ffffff01803d6688 00000000fee20000      24k      12k [ anon ]
ffffff01803d68c8 00000000fee30000       4k       4k [ anon ]
ffffff01803d6bc8 00000000fee40000    1216k     940k /usr/lib/libc/libc_hwcap1.so
ffffff01803d6868 00000000fef70000      36k      36k /usr/lib/libc/libc_hwcap1.so
ffffff01807de1a0 00000000fef79000       8k       8k [ anon ]
ffffff01807de200 00000000fef80000       4k       4k [ anon ]
ffffff01807de140 00000000fef90000       4k       4k [ anon ]
ffffff01807de260 00000000fefa0000       4k       4k [ anon ]
ffffff01807de2c0 00000000fefb0000       4k       4k [ anon ]
ffffff01807de020 00000000fefb7000     208k     208k /lib/ld.so.1
ffffff015b8afc40 00000000feffb000       8k       8k /lib/ld.so.1
ffffff01807de320 00000000feffd000       4k       4k [ anon ]

> ffffff01803d6628::print -t seg_t s_data | ::print -t 'struct segvn_data' amp | ::print -t 'struct anon_map' ahp | ::print -t 'struct anon_hdr' array_chunk 
void **array_chunk = 0xffffff017fb25dc0

Now all three slots of the anon_map / anon_hdr are filled:

> 0xffffff017fb25dc0,3/K
0xffffff017fb25dc0:             ffffff01803d7d10 ffffff01803d7c20 ffffff01803d40c0 

Examining them one by one:

> ffffff01803d7d10::print -t 'struct anon'
struct anon {
    struct vnode *an_vp = 0xffffff014d3ba900
    struct vnode *an_pvp = 0
    anoff_t an_off = 0x1fffffe03007a000
    anoff_t an_poff = 0
    struct anon *an_hash = 0
    int an_refcnt = 0x1
}

> ffffff01803d7d10::print -t 'struct anon' an_vp | ::print -t vnode_t v_pages
struct page *v_pages = 0xffffff0001aecb98

> ffffff01803d7d10::print -t 'struct anon' an_vp | ::print -t vnode_t v_pages | ::print page_t p_pagenum
p_pagenum = 0x35abf

And looking at the physical page, thanks for MDB's ease of doing so:

> 0x35abf000,10\K
0x35abf000:     deadbeef        0               0               0               
                0               0               0               0               
                0               0               0               0               
                0               0               0               0               

> ffffff01803d7c20::print -t 'struct anon' an_vp | ::print -t vnode_t v_pages | ::print page_t p_pagenum
p_pagenum = 0x40740

and the next page:

> 0x40740000,5\K
0x40740000:     deadbeef00      0               0               0             0               

and the one after:

> ffffff01803d40c0::print -t 'struct anon' an_vp | ::print -t vnode_t v_pages | ::print page_t p_pagenum
p_pagenum = 0x40d41
> 0x40d41000,5\K
0x40d41000:     deadbeef0000    0               0               0              0               

------- this concludes our walk of an anonymous mmap-ed allocation.

