Advanced Memory Analysis
This section describes facilities for performing advanced memory analysis, including locating memory leaks
and sources of data corruption.
Finding Memory Leaks
The ::findleaks dcmd provides powerful and efficient detection of memory leaks in kernel
crash dumps where the full set of kmem debug features has been enabled.
The first execution of ::findleaks processes the dump for memory leaks (this can
take a few minutes), and then coalesces the leaks by the allocation stack
trace. The findleaks report shows a bufctl address and the topmost stack
frame for each memory leak that was identified:
> ::findleaks
CACHE LEAKED BUFCTL CALLER
70039ba8 1 703746c0 pm_autoconfig+0x708
70039ba8 1 703748a0 pm_autoconfig+0x708
7003a028 1 70d3b1a0 sigaddq+0x108
7003c7a8 1 70515200 pm_ioctl+0x187c
------------------------------------------------------
Total 4 buffers, 376 bytes
Using the bufctl pointers, you can obtain the complete stack backtrace of the
allocation by applying the bufctl_audit macro:
> 70d3b1a0$<bufctl_audit
0x70d3b1a0: next addr slab
70a049c0 70d03b28 70bb7480
0x70d3b1ac: cache timestamp thread
7003a028 13f7cf63b3 70b38380
0x70d3b1bc: lastlog contents stackdepth
700d6e60 0 5
0x70d3b1c8:
kmem_alloc+0x30
sigaddq+0x108
sigsendproc+0x210
sigqkill+0x90
kill+0x28
The programmer can usually use the bufctl_audit information and the allocation stack trace
to quickly track down the code path that leaks the given buffer.
Finding References to Data
When trying to diagnose a memory corruption problem, you should know what other
kernel entities hold a copy of a particular pointer. This is important because
it can reveal which thread accessed a data structure after it was freed.
It can also make it easier to understand what kernel entities are
sharing knowledge of a particular (valid) data item. The ::whatis and
::kgrep dcmds can be used to answer these questions. You can apply
::whatis to a value of interest:
> 0x705d8640::whatis
705d8640 is 705d8640+0, allocated from streams_mblk
In this case, 0x705d8640 is revealed to be a pointer to a STREAMS
mblk structure. To see the entire allocation tree, use ::whatis -a instead:
> 0x705d8640::whatis -a
705d8640 is 705d8640+0, allocated from streams_mblk
705d8640 is 705d8000+640, allocated from kmem_va_8192
705d8640 is 705d8000+640 from kmem_default vmem arena
705d8640 is 705d2000+2640 from kmem_va vmem arena
705d8640 is 705d2000+2640 from heap vmem arena
This reveals that the allocation also appears in the kmem_va_8192 cache. The
kmem_va_8192 cache is a kmem cache that is fronting the kmem_va vmem arena.
It also shows the full stack of vmem allocations.
The complete list of kmem caches and vmem arenas is displayed by
the ::kmastat dcmd. You can use ::kgrep to locate other kernel addresses
that contain a pointer to this mblk. This illustrates the hierarchical nature of
memory allocations in the system; in general, you can determine the type of
object referred to by the given address from the name of the most
specific kmem cache.
> 0x705d8640::kgrep
400a3720
70580d24
7069d7f0
706a37ec
706add34
and investigate them by applying ::whatis again:
> 400a3720::whatis
400a3720 is in thread 7095b240's stack
> 706add34::whatis
706add34 is 706add20+14, allocated from streams_dblk_120
Here one pointer is located on the stack of a known kernel
thread, and another is the mblk pointer inside of the corresponding STREAMS dblk structure.
Finding Corrupt Buffers With ::kmem_verify
MDB's ::kmem_verify dcmd implements most of the same checks that the kmem allocator
does at runtime. ::kmem_verify can be invoked in order to scan every
kmem cache with appropriate kmem_flags, or to examine a particular cache.
Here is an example of using ::kmem_verify to isolate a problem:
> ::kmem_verify
Cache Name Addr Cache Integrity
kmem_alloc_8 70039428 clean
kmem_alloc_16 700396a8 clean
kmem_alloc_24 70039928 1 corrupt buffer
kmem_alloc_32 70039ba8 clean
kmem_alloc_40 7003a028 clean
kmem_alloc_48 7003a2a8 clean
...
It is easy to see here that the kmem_alloc_24 cache contains what ::kmem_verify
believes to be a problem. With an explicit cache argument, the ::kmem_verify
dcmd provides more detailed information about the problem:
> 70039928::kmem_verify
Summary for cache 'kmem_alloc_24'
buffer 702babc0 (free) seems corrupted, at 702babc0
The next step is to examine the buffer which ::kmem_verify believes to be
corrupt:
> 0x702babc0,5/KKn
0x702babc0: 0 deadbeef
deadbeef deadbeef
deadbeef deadbeef
feedface feedface
703785a0 84d9714e
The reason that ::kmem_verify flagged this buffer is now clear: The first word
in the buffer (at 0x702babc0) should probably be filled with the 0xdeadbeef pattern, not
with a 0. At this point, examining the bufctl_audit for this
buffer might yield clues about what code recently wrote to the buffer, indicating
where and when it was freed.
Another useful technique in this situation is to use ::kgrep to search the
address space for references to address 0x702babc0, in order to discover what threads
or data structures are still holding references to this freed data.
Allocator Logging Facility
When KMF_AUDIT is set for a cache, the kernel memory allocator maintains a
log that records the recent history of its activity. This transaction log records
bufctl_audit records. If the KMF_AUDIT and the KMF_CONTENTS flags are both set,
the allocator generates a contents log that records portions of the actual contents
of allocated and freed buffers. The structure and use of the contents
log is outside the scope of this document. The transaction log is discussed
in this section.
MDB provides several facilities for displaying the transaction log. The simplest is
::walk kmem_log, which prints out the transaction in the log as a series of
bufctl_audit_t pointers:
> ::walk kmem_log
70128340
701282e0
70128280
70128220
701281c0
...
> 70128340$<bufctl_audit
0x70128340: next addr slab
70ac1d40 70bc4ea8 70bb7c00
0x7012834c: cache timestamp thread
70039428 e1bd7abe721 70aacde0
0x7012835c: lastlog contents stackdepth
701282e0 7018f340 4
0x70128368:
kmem_cache_free+0x24
nfs3_sync+0x3c
vfs_sync+0x84
syssync+4
A more elegant way to view the entire transaction log is by using
the ::kmem_log command:
> ::kmem_log
CPU ADDR BUFADDR TIMESTAMP THREAD
0 70128340 70bc4ea8 e1bd7abe721 70aacde0
0 701282e0 70bc4ea8 e1bd7aa86fa 70aacde0
0 70128280 70bc4ea8 e1bd7aa27dd 70aacde0
0 70128220 70bc4ea8 e1bd7a98a6e 70aacde0
0 701281c0 70d03738 e1bd7a8e3e0 70aacde0
...
0 70127140 70cf78a0 e1bd78035ad 70aacde0
0 701270e0 709cf6c0 e1bd6d2573a 40033e60
0 70127080 70cedf20 e1bd6d1e984 40033e60
0 70127020 70b09578 e1bd5fc1791 40033e60
0 70126fc0 70cf78a0 e1bd5fb6b5a 40033e60
0 70126f60 705ed388 e1bd5fb080d 40033e60
0 70126f00 705ed388 e1bd551ff73 70aacde0
...
The output of ::kmem_log is sorted in descending order by timestamp. The ADDR
column is the bufctl_audit structure corresponding to that transaction; BUFADDR points to the
actual buffer.
These figures represent transactions on buffers (both allocations and frees). When a particular
buffer is corrupted, it can be helpful to locate that buffer in the
transaction log, then determine in which other transactions the transacting thread was involved.
This can help to assemble a picture of the sequence of events
that occurred prior to and after the allocation (or free) of a buffer.
You can employ the ::bufctl command to filter the output of walking the
transaction log. The ::bufctl -a command filters the buffers in the transaction
log by buffer address. This example filters on buffer 0x70b09578:
> ::walk kmem_log | ::bufctl -a 0x70b09578
ADDR BUFADDR TIMESTAMP THREAD CALLER
70127020 70b09578 e1bd5fc1791 40033e60 biodone+0x108
70126e40 70b09578 e1bd55062da 70aacde0 pageio_setup+0x268
70126de0 70b09578 e1bd52b2317 40033e60 biodone+0x108
70126c00 70b09578 e1bd497ee8e 70aacde0 pageio_setup+0x268
70120480 70b09578 e1bd21c5e2a 70aacde0 elfexec+0x9f0
70120060 70b09578 e1bd20f5ab5 70aacde0 getelfhead+0x100
7011ef20 70b09578 e1bd1e9a1dd 70aacde0 ufs_getpage_miss+0x354
7011d720 70b09578 e1bd1170dc4 70aacde0 pageio_setup+0x268
70117d80 70b09578 e1bcff6ff27 70bc2480 elfexec+0x9f0
70117960 70b09578 e1bcfea4a9f 70bc2480 getelfhead+0x100
...
This example illustrates that a particular buffer can be used in numerous transactions.
Note - Remember that the kmem transaction log is an incomplete record of the transactions
made by the kernel memory allocator. Older entries in the log are evicted
as needed in order to keep the size of the log constant.
The ::allocdby and ::freedby dcmds provide a convenient way to summarize transactions associated
with a particular thread. Here is an example of listing the recent
allocations performed by thread 0x70aacde0:
> 0x70aacde0::allocdby
BUFCTL TIMESTAMP CALLER
70d4d8c0 e1edb14511a allocb+0x88
70d4e8a0 e1edb142472 dblk_constructor+0xc
70d4a240 e1edb13dd4f allocb+0x88
70d4e840 e1edb13aeec dblk_constructor+0xc
70d4d860 e1ed8344071 allocb+0x88
70d4e7e0 e1ed8342536 dblk_constructor+0xc
70d4a1e0 e1ed82b3a3c allocb+0x88
70a53f80 e1ed82b0b91 dblk_constructor+0xc
70d4d800 e1e9b663b92 allocb+0x88
By examining bufctl_audit records, you can understand the recent activities of a particular
thread.