1 /* This file implements block level tracing support. */ 2 3 #include <minix/drivers.h> 4 #include <minix/blockdriver_mt.h> 5 #include <minix/btrace.h> 6 #include <sys/ioc_block.h> 7 #include <minix/minlib.h> 8 #include <assert.h> 9 10 #include "const.h" 11 #include "trace.h" 12 13 #define NO_TRACEDEV ((devminor_t) -1) 14 #define NO_TIME ((u32_t) -1) 15 16 static int trace_enabled = FALSE; 17 static devminor_t trace_dev = NO_TRACEDEV; 18 static btrace_entry *trace_buf = NULL; 19 static size_t trace_size = 0; 20 static size_t trace_pos; 21 static size_t trace_next; 22 static u64_t trace_tsc; 23 24 /* Pointers to in-progress trace entries for each thread (all worker threads, 25 * plus one for the main thread). Each pointer is set to NULL whenever no 26 * operation is currently being traced for that thread, for whatever reason. 27 */ 28 static btrace_entry *trace_ptr[MAX_THREADS + 1] = { NULL }; 29 30 /*===========================================================================* 31 * trace_gettime * 32 *===========================================================================*/ 33 static u32_t trace_gettime(void) 34 { 35 /* Return the current time, in microseconds since the start of the trace. 36 */ 37 u64_t tsc; 38 39 assert(trace_enabled); 40 41 read_tsc_64(&tsc); 42 43 tsc -= trace_tsc; 44 45 return tsc_64_to_micros(tsc); 46 } 47 48 /*===========================================================================* 49 * trace_ctl * 50 *===========================================================================*/ 51 int trace_ctl(devminor_t minor, unsigned long request, endpoint_t endpt, 52 cp_grant_id_t grant) 53 { 54 /* Process a block trace control request. 55 */ 56 size_t size; 57 int r, ctl, entries; 58 59 switch (request) { 60 case BIOCTRACEBUF: 61 /* The size cannot be changed when tracing is enabled. */ 62 if (trace_enabled) return EBUSY; 63 64 /* Copy in the requested size. */ 65 if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &size, 66 sizeof(size))) != OK) 67 return r; 68 69 if (size >= INT_MAX / sizeof(btrace_entry)) return EINVAL; 70 71 /* The size can only be set or reset, not changed. */ 72 if (size > 0 && trace_size > 0) return EBUSY; 73 74 /* Allocate or free a buffer for tracing data. For future multi-device 75 * tracing support, the buffer is associated with a minor device. 76 */ 77 if (size == 0) { 78 if (trace_dev == NO_TRACEDEV) return OK; 79 80 if (trace_dev != minor) return EINVAL; 81 82 free(trace_buf); 83 84 trace_dev = NO_TRACEDEV; 85 } else { 86 if ((trace_buf = malloc(size * sizeof(btrace_entry))) == NULL) 87 return errno; 88 89 trace_dev = minor; 90 } 91 92 trace_size = size; 93 trace_pos = 0; 94 trace_next = 0; 95 96 return OK; 97 98 case BIOCTRACECTL: 99 /* We can only start/stop tracing if the given device has a trace 100 * buffer associated with it. 101 */ 102 if (trace_dev != minor) return EINVAL; 103 104 /* Copy in the request code. */ 105 if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &ctl, 106 sizeof(ctl))) != OK) 107 return r; 108 109 /* Start or stop tracing. */ 110 switch (ctl) { 111 case BTCTL_START: 112 if (trace_enabled) return EBUSY; 113 114 read_tsc_64(&trace_tsc); 115 116 trace_enabled = TRUE; 117 118 break; 119 120 case BTCTL_STOP: 121 if (!trace_enabled) return EINVAL; 122 123 trace_enabled = FALSE; 124 125 /* Cancel all ongoing trace operations. */ 126 memset(trace_ptr, 0, sizeof(trace_ptr)); 127 128 break; 129 130 default: 131 return EINVAL; 132 } 133 134 return OK; 135 136 case BIOCTRACEGET: 137 /* We can only retrieve tracing entries if the given device has a trace 138 * buffer associated with it. 139 */ 140 if (trace_dev != minor) return EINVAL; 141 142 if (trace_enabled) return EBUSY; 143 144 /* How much can we copy out? */ 145 entries = MIN(trace_pos - trace_next, 146 _MINIX_IOCTL_SIZE_BIG(request) / sizeof(btrace_entry)); 147 148 if (entries == 0) 149 return 0; 150 151 if ((r = sys_safecopyto(endpt, grant, 0, 152 (vir_bytes) &trace_buf[trace_next], 153 entries * sizeof(btrace_entry))) != OK) 154 return r; 155 156 trace_next += entries; 157 158 return entries; 159 } 160 161 return EINVAL; 162 } 163 164 /*===========================================================================* 165 * trace_start * 166 *===========================================================================*/ 167 void trace_start(thread_id_t id, message *m_ptr) 168 { 169 /* Start creating a trace entry. 170 */ 171 btrace_entry *entry; 172 int req; 173 u64_t pos; 174 size_t size; 175 int flags; 176 177 if (!trace_enabled || trace_dev != m_ptr->m_lbdev_lblockdriver_msg.minor) return; 178 179 assert(id >= 0 && id < MAX_THREADS + 1); 180 181 if (trace_pos == trace_size) 182 return; 183 184 switch (m_ptr->m_type) { 185 case BDEV_OPEN: req = BTREQ_OPEN; break; 186 case BDEV_CLOSE: req = BTREQ_CLOSE; break; 187 case BDEV_READ: req = BTREQ_READ; break; 188 case BDEV_WRITE: req = BTREQ_WRITE; break; 189 case BDEV_GATHER: req = BTREQ_GATHER; break; 190 case BDEV_SCATTER: req = BTREQ_SCATTER; break; 191 case BDEV_IOCTL: req = BTREQ_IOCTL; break; 192 default: return; 193 } 194 195 switch (m_ptr->m_type) { 196 case BDEV_OPEN: 197 case BDEV_CLOSE: 198 pos = 0; 199 size = m_ptr->m_lbdev_lblockdriver_msg.access; 200 flags = 0; 201 202 break; 203 204 case BDEV_READ: 205 case BDEV_WRITE: 206 case BDEV_GATHER: 207 case BDEV_SCATTER: 208 pos = m_ptr->m_lbdev_lblockdriver_msg.pos; 209 size = m_ptr->m_lbdev_lblockdriver_msg.count; 210 flags = m_ptr->m_lbdev_lblockdriver_msg.flags; 211 212 break; 213 214 case BDEV_IOCTL: 215 pos = 0; 216 size = m_ptr->m_lbdev_lblockdriver_msg.request; 217 flags = 0; 218 219 /* Do not log trace control requests. */ 220 switch (size) { 221 case BIOCTRACEBUF: 222 case BIOCTRACECTL: 223 case BIOCTRACEGET: 224 return; 225 } 226 227 break; 228 229 default: 230 /* Do not log any other messages. */ 231 return; 232 } 233 234 entry = &trace_buf[trace_pos]; 235 entry->request = req; 236 entry->size = size; 237 entry->position = pos; 238 entry->flags = flags; 239 entry->result = BTRES_INPROGRESS; 240 entry->start_time = trace_gettime(); 241 entry->finish_time = NO_TIME; 242 243 trace_ptr[id] = entry; 244 trace_pos++; 245 } 246 247 /*===========================================================================* 248 * trace_setsize * 249 *===========================================================================*/ 250 void trace_setsize(thread_id_t id, size_t size) 251 { 252 /* Set the current trace entry's actual (byte) size, for vector requests. 253 */ 254 btrace_entry *entry; 255 256 if (!trace_enabled) return; 257 258 assert(id >= 0 && id < MAX_THREADS + 1); 259 260 if ((entry = trace_ptr[id]) == NULL) return; 261 262 entry->size = size; 263 } 264 265 /*===========================================================================* 266 * trace_finish * 267 *===========================================================================*/ 268 void trace_finish(thread_id_t id, int result) 269 { 270 /* Finish a trace entry. 271 */ 272 btrace_entry *entry; 273 274 if (!trace_enabled) return; 275 276 assert(id >= 0 && id < MAX_THREADS + 1); 277 278 if ((entry = trace_ptr[id]) == NULL) return; 279 280 entry->result = result; 281 entry->finish_time = trace_gettime(); 282 283 trace_ptr[id] = NULL; 284 } 285