xref: /illumos-gate/usr/src/cmd/mdb/common/modules/mm/mm.c (revision fd6d41c5025e9fb45a115fc82d86e9983d1e9fd6)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2015, Joyent, Inc.  All rights reserved.
14  */
15 
16 #include <sys/mdb_modapi.h>
17 #include <sys/time.h>
18 #include <sys/mem.h>
19 
20 typedef struct kmemlog_walk {
21 	uintptr_t kmlw_addr;
22 	mm_logentry_t *kmlw_entries;
23 	int kmlw_nentries;
24 	int kmlw_entry;
25 	int kmlw_oldest;
26 } kmemlog_walk_t;
27 
28 static int
29 kmemlog_walk_init(mdb_walk_state_t *wsp)
30 {
31 	kmemlog_walk_t *kw;
32 	GElf_Sym sym;
33 
34 	if (mdb_lookup_by_name("mm_kmemlog", &sym) != 0) {
35 		mdb_warn("couldn't find symbol 'mm_kmemlog'");
36 		return (WALK_ERR);
37 	}
38 
39 	kw = mdb_zalloc(sizeof (kmemlog_walk_t), UM_SLEEP);
40 	kw->kmlw_entries = mdb_zalloc(sym.st_size, UM_SLEEP);
41 	kw->kmlw_addr = sym.st_value;
42 
43 	if (mdb_vread(kw->kmlw_entries, sym.st_size, sym.st_value) == -1) {
44 		mdb_warn("couldn't read log at %p", sym.st_value);
45 		mdb_free(kw->kmlw_entries, sym.st_size);
46 		mdb_free(kw, sizeof (kmemlog_walk_t));
47 		return (WALK_ERR);
48 	}
49 
50 	kw->kmlw_nentries = sym.st_size / sizeof (mm_logentry_t);
51 
52 	mdb_readvar(&kw->kmlw_entry, "mm_kmemlogent");
53 	kw->kmlw_oldest = kw->kmlw_entry;
54 	wsp->walk_data = kw;
55 
56 	return (WALK_NEXT);
57 }
58 
59 static int
60 kmemlog_walk_step(mdb_walk_state_t *wsp)
61 {
62 	kmemlog_walk_t *kw = wsp->walk_data;
63 	mm_logentry_t *ent;
64 	int rval = WALK_NEXT;
65 
66 	ent = &kw->kmlw_entries[kw->kmlw_entry];
67 
68 	if (++kw->kmlw_entry == kw->kmlw_nentries)
69 		kw->kmlw_entry = 0;
70 
71 	if (ent->mle_hrtime != 0) {
72 		rval = wsp->walk_callback(kw->kmlw_addr + ((uintptr_t)ent -
73 		    (uintptr_t)kw->kmlw_entries), ent, wsp->walk_cbdata);
74 	}
75 
76 	if (rval == WALK_NEXT && kw->kmlw_entry == kw->kmlw_oldest)
77 		return (WALK_DONE);
78 
79 	return (rval);
80 }
81 
82 static void
83 kmemlog_walk_fini(mdb_walk_state_t *wsp)
84 {
85 	kmemlog_walk_t *kw = wsp->walk_data;
86 
87 	mdb_free(kw->kmlw_entries, kw->kmlw_nentries * sizeof (mm_logentry_t));
88 	mdb_free(kw, sizeof (kmemlog_walk_t));
89 }
90 
91 static int
92 kmemlog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
93 {
94 	mm_logentry_t ent;
95 
96 	if (!(flags & DCMD_ADDRSPEC)) {
97 		if (mdb_walk_dcmd("kmemlog", "kmemlog", argc, argv) == -1) {
98 			mdb_warn("can't walk 'kmemlog'");
99 			return (DCMD_ERR);
100 		}
101 		return (DCMD_OK);
102 	}
103 
104 	if (DCMD_HDRSPEC(flags)) {
105 		mdb_printf("%?s %-20s %?s %5s %s\n",
106 		    "ADDR", "TIME", "VADDR", "PID", "PSARGS");
107 	}
108 
109 	if (mdb_vread(&ent, sizeof (ent), addr) == -1) {
110 		mdb_warn("can't read mm_logentry_t at %p", addr);
111 		return (DCMD_ERR);
112 	}
113 
114 	mdb_printf("%?p %-20Y %?p %5d %s\n",
115 	    addr, ent.mle_hrestime.tv_sec, ent.mle_vaddr, ent.mle_pid,
116 	    ent.mle_psargs);
117 
118 	return (DCMD_OK);
119 }
120 
121 static const mdb_dcmd_t dcmds[] = {
122 	{ "kmemlog", NULL, "print log of writes via /dev/kmem", kmemlog },
123 	{ NULL }
124 };
125 
126 static const mdb_walker_t walkers[] = {
127 	{ "kmemlog", "walk entries in /dev/kmem write log",
128 		kmemlog_walk_init, kmemlog_walk_step, kmemlog_walk_fini },
129 	{ NULL }
130 };
131 
132 static const mdb_modinfo_t modinfo = {
133 	MDB_API_VERSION, dcmds, walkers
134 };
135 
136 const mdb_modinfo_t *
137 _mdb_init(void)
138 {
139 	return (&modinfo);
140 }
141