xref: /minix/minix/lib/libblockdriver/trace.c (revision 0a6a1f1d)
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