1915c848Bryan Cantrill/*
2915c848Bryan Cantrill * This file and its contents are supplied under the terms of the
3915c848Bryan Cantrill * Common Development and Distribution License ("CDDL"), version 1.0.
4915c848Bryan Cantrill * You may only use this file in accordance with the terms of version
5915c848Bryan Cantrill * 1.0 of the CDDL.
6915c848Bryan Cantrill *
7915c848Bryan Cantrill * A full copy of the text of the CDDL should have accompanied this
8915c848Bryan Cantrill * source.  A copy of the CDDL is also available via the Internet at
9915c848Bryan Cantrill * http://www.illumos.org/license/CDDL.
10915c848Bryan Cantrill */
11915c848Bryan Cantrill
12915c848Bryan Cantrill/*
13915c848Bryan Cantrill * Copyright (c) 2015, Joyent, Inc.  All rights reserved.
14915c848Bryan Cantrill */
15915c848Bryan Cantrill
16915c848Bryan Cantrill#include <sys/mdb_modapi.h>
17915c848Bryan Cantrill#include <sys/time.h>
18915c848Bryan Cantrill#include <sys/mem.h>
19915c848Bryan Cantrill
20915c848Bryan Cantrilltypedef struct kmemlog_walk {
21915c848Bryan Cantrill	uintptr_t kmlw_addr;
22915c848Bryan Cantrill	mm_logentry_t *kmlw_entries;
23915c848Bryan Cantrill	int kmlw_nentries;
24915c848Bryan Cantrill	int kmlw_entry;
25915c848Bryan Cantrill	int kmlw_oldest;
26915c848Bryan Cantrill} kmemlog_walk_t;
27915c848Bryan Cantrill
28915c848Bryan Cantrillstatic int
29915c848Bryan Cantrillkmemlog_walk_init(mdb_walk_state_t *wsp)
30915c848Bryan Cantrill{
31915c848Bryan Cantrill	kmemlog_walk_t *kw;
32915c848Bryan Cantrill	GElf_Sym sym;
33915c848Bryan Cantrill
34915c848Bryan Cantrill	if (mdb_lookup_by_name("mm_kmemlog", &sym) != 0) {
35915c848Bryan Cantrill		mdb_warn("couldn't find symbol 'mm_kmemlog'");
36915c848Bryan Cantrill		return (WALK_ERR);
37915c848Bryan Cantrill	}
38915c848Bryan Cantrill
39915c848Bryan Cantrill	kw = mdb_zalloc(sizeof (kmemlog_walk_t), UM_SLEEP);
40915c848Bryan Cantrill	kw->kmlw_entries = mdb_zalloc(sym.st_size, UM_SLEEP);
41915c848Bryan Cantrill	kw->kmlw_addr = sym.st_value;
42915c848Bryan Cantrill
43915c848Bryan Cantrill	if (mdb_vread(kw->kmlw_entries, sym.st_size, sym.st_value) == -1) {
44915c848Bryan Cantrill		mdb_warn("couldn't read log at %p", sym.st_value);
45915c848Bryan Cantrill		mdb_free(kw->kmlw_entries, sym.st_size);
46915c848Bryan Cantrill		mdb_free(kw, sizeof (kmemlog_walk_t));
47915c848Bryan Cantrill		return (WALK_ERR);
48915c848Bryan Cantrill	}
49915c848Bryan Cantrill
50915c848Bryan Cantrill	kw->kmlw_nentries = sym.st_size / sizeof (mm_logentry_t);
51915c848Bryan Cantrill
52915c848Bryan Cantrill	mdb_readvar(&kw->kmlw_entry, "mm_kmemlogent");
53915c848Bryan Cantrill	kw->kmlw_oldest = kw->kmlw_entry;
54915c848Bryan Cantrill	wsp->walk_data = kw;
55915c848Bryan Cantrill
56915c848Bryan Cantrill	return (WALK_NEXT);
57915c848Bryan Cantrill}
58915c848Bryan Cantrill
59915c848Bryan Cantrillstatic int
60915c848Bryan Cantrillkmemlog_walk_step(mdb_walk_state_t *wsp)
61915c848Bryan Cantrill{
62915c848Bryan Cantrill	kmemlog_walk_t *kw = wsp->walk_data;
63915c848Bryan Cantrill	mm_logentry_t *ent;
64915c848Bryan Cantrill	int rval = WALK_NEXT;
65915c848Bryan Cantrill
66915c848Bryan Cantrill	ent = &kw->kmlw_entries[kw->kmlw_entry];
67915c848Bryan Cantrill
68915c848Bryan Cantrill	if (++kw->kmlw_entry == kw->kmlw_nentries)
69915c848Bryan Cantrill		kw->kmlw_entry = 0;
70915c848Bryan Cantrill
71915c848Bryan Cantrill	if (ent->mle_hrtime != 0) {
72915c848Bryan Cantrill		rval = wsp->walk_callback(kw->kmlw_addr + ((uintptr_t)ent -
73915c848Bryan Cantrill		    (uintptr_t)kw->kmlw_entries), ent, wsp->walk_cbdata);
74915c848Bryan Cantrill	}
75915c848Bryan Cantrill
76915c848Bryan Cantrill	if (rval == WALK_NEXT && kw->kmlw_entry == kw->kmlw_oldest)
77915c848Bryan Cantrill		return (WALK_DONE);
78915c848Bryan Cantrill
79915c848Bryan Cantrill	return (rval);
80915c848Bryan Cantrill}
81915c848Bryan Cantrill
82915c848Bryan Cantrillstatic void
83915c848Bryan Cantrillkmemlog_walk_fini(mdb_walk_state_t *wsp)
84915c848Bryan Cantrill{
85915c848Bryan Cantrill	kmemlog_walk_t *kw = wsp->walk_data;
86915c848Bryan Cantrill
87915c848Bryan Cantrill	mdb_free(kw->kmlw_entries, kw->kmlw_nentries * sizeof (mm_logentry_t));
88915c848Bryan Cantrill	mdb_free(kw, sizeof (kmemlog_walk_t));
89915c848Bryan Cantrill}
90915c848Bryan Cantrill
91915c848Bryan Cantrillstatic int
92915c848Bryan Cantrillkmemlog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
93915c848Bryan Cantrill{
94915c848Bryan Cantrill	mm_logentry_t ent;
95915c848Bryan Cantrill
96915c848Bryan Cantrill	if (!(flags & DCMD_ADDRSPEC)) {
97915c848Bryan Cantrill		if (mdb_walk_dcmd("kmemlog", "kmemlog", argc, argv) == -1) {
98915c848Bryan Cantrill			mdb_warn("can't walk 'kmemlog'");
99915c848Bryan Cantrill			return (DCMD_ERR);
100915c848Bryan Cantrill		}
101915c848Bryan Cantrill		return (DCMD_OK);
102915c848Bryan Cantrill	}
103915c848Bryan Cantrill
104915c848Bryan Cantrill	if (DCMD_HDRSPEC(flags)) {
105915c848Bryan Cantrill		mdb_printf("%?s %-20s %?s %5s %s\n",
106915c848Bryan Cantrill		    "ADDR", "TIME", "VADDR", "PID", "PSARGS");
107915c848Bryan Cantrill	}
108915c848Bryan Cantrill
109915c848Bryan Cantrill	if (mdb_vread(&ent, sizeof (ent), addr) == -1) {
110915c848Bryan Cantrill		mdb_warn("can't read mm_logentry_t at %p", addr);
111915c848Bryan Cantrill		return (DCMD_ERR);
112915c848Bryan Cantrill	}
113915c848Bryan Cantrill
114915c848Bryan Cantrill	mdb_printf("%?p %-20Y %?p %5d %s\n",
115915c848Bryan Cantrill	    addr, ent.mle_hrestime.tv_sec, ent.mle_vaddr, ent.mle_pid,
116915c848Bryan Cantrill	    ent.mle_psargs);
117915c848Bryan Cantrill
118915c848Bryan Cantrill	return (DCMD_OK);
119915c848Bryan Cantrill}
120915c848Bryan Cantrill
121915c848Bryan Cantrillstatic const mdb_dcmd_t dcmds[] = {
122915c848Bryan Cantrill	{ "kmemlog", NULL, "print log of writes via /dev/kmem", kmemlog },
123915c848Bryan Cantrill	{ NULL }
124915c848Bryan Cantrill};
125915c848Bryan Cantrill
126915c848Bryan Cantrillstatic const mdb_walker_t walkers[] = {
127915c848Bryan Cantrill	{ "kmemlog", "walk entries in /dev/kmem write log",
128915c848Bryan Cantrill		kmemlog_walk_init, kmemlog_walk_step, kmemlog_walk_fini },
129915c848Bryan Cantrill	{ NULL }
130915c848Bryan Cantrill};
131915c848Bryan Cantrill
132915c848Bryan Cantrillstatic const mdb_modinfo_t modinfo = {
133915c848Bryan Cantrill	MDB_API_VERSION, dcmds, walkers
134915c848Bryan Cantrill};
135915c848Bryan Cantrill
136915c848Bryan Cantrillconst mdb_modinfo_t *
137915c848Bryan Cantrill_mdb_init(void)
138915c848Bryan Cantrill{
139915c848Bryan Cantrill	return (&modinfo);
140915c848Bryan Cantrill}