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