xref: /minix3/minix/lib/libblockdriver/trace.c (revision 65f76edb8f568553a5b4386b0c4917803d47e97d)
1433d6423SLionel Sambuc /* This file implements block level tracing support. */
2433d6423SLionel Sambuc 
3433d6423SLionel Sambuc #include <minix/drivers.h>
4433d6423SLionel Sambuc #include <minix/blockdriver_mt.h>
5433d6423SLionel Sambuc #include <minix/btrace.h>
6433d6423SLionel Sambuc #include <sys/ioc_block.h>
7433d6423SLionel Sambuc #include <minix/minlib.h>
8433d6423SLionel Sambuc #include <assert.h>
9433d6423SLionel Sambuc 
10433d6423SLionel Sambuc #include "const.h"
11433d6423SLionel Sambuc #include "trace.h"
12433d6423SLionel Sambuc 
13*65f76edbSDavid van Moolenbroek #define NO_TRACEDEV		((devminor_t) -1)
14433d6423SLionel Sambuc #define NO_TIME			((u32_t) -1)
15433d6423SLionel Sambuc 
16433d6423SLionel Sambuc static int trace_enabled	= FALSE;
17*65f76edbSDavid van Moolenbroek static devminor_t trace_dev	= NO_TRACEDEV;
18433d6423SLionel Sambuc static btrace_entry *trace_buf	= NULL;
19433d6423SLionel Sambuc static size_t trace_size	= 0;
20433d6423SLionel Sambuc static size_t trace_pos;
21433d6423SLionel Sambuc static size_t trace_next;
22433d6423SLionel Sambuc static u64_t trace_tsc;
23433d6423SLionel Sambuc 
24433d6423SLionel Sambuc /* Pointers to in-progress trace entries for each thread (all worker threads,
25433d6423SLionel Sambuc  * plus one for the main thread). Each pointer is set to NULL whenever no
26433d6423SLionel Sambuc  * operation is currently being traced for that thread, for whatever reason.
27433d6423SLionel Sambuc  */
28433d6423SLionel Sambuc static btrace_entry *trace_ptr[MAX_THREADS + 1] = { NULL };
29433d6423SLionel Sambuc 
30433d6423SLionel Sambuc /*===========================================================================*
31433d6423SLionel Sambuc  *				trace_gettime				     *
32433d6423SLionel Sambuc  *===========================================================================*/
trace_gettime(void)33433d6423SLionel Sambuc static u32_t trace_gettime(void)
34433d6423SLionel Sambuc {
35433d6423SLionel Sambuc /* Return the current time, in microseconds since the start of the trace.
36433d6423SLionel Sambuc  */
37433d6423SLionel Sambuc   u64_t tsc;
38433d6423SLionel Sambuc 
39433d6423SLionel Sambuc   assert(trace_enabled);
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc   read_tsc_64(&tsc);
42433d6423SLionel Sambuc 
43433d6423SLionel Sambuc   tsc -= trace_tsc;
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc   return tsc_64_to_micros(tsc);
46433d6423SLionel Sambuc }
47433d6423SLionel Sambuc 
48433d6423SLionel Sambuc /*===========================================================================*
49433d6423SLionel Sambuc  *				trace_ctl				     *
50433d6423SLionel Sambuc  *===========================================================================*/
trace_ctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant)51*65f76edbSDavid van Moolenbroek int trace_ctl(devminor_t minor, unsigned long request, endpoint_t endpt,
52433d6423SLionel Sambuc 	cp_grant_id_t grant)
53433d6423SLionel Sambuc {
54433d6423SLionel Sambuc /* Process a block trace control request.
55433d6423SLionel Sambuc  */
56433d6423SLionel Sambuc   size_t size;
57433d6423SLionel Sambuc   int r, ctl, entries;
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc   switch (request) {
60433d6423SLionel Sambuc   case BIOCTRACEBUF:
61433d6423SLionel Sambuc 	/* The size cannot be changed when tracing is enabled. */
62433d6423SLionel Sambuc 	if (trace_enabled) return EBUSY;
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc 	/* Copy in the requested size. */
65433d6423SLionel Sambuc 	if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &size,
66433d6423SLionel Sambuc 		sizeof(size))) != OK)
67433d6423SLionel Sambuc 		return r;
68433d6423SLionel Sambuc 
69433d6423SLionel Sambuc 	if (size >= INT_MAX / sizeof(btrace_entry)) return EINVAL;
70433d6423SLionel Sambuc 
71433d6423SLionel Sambuc 	/* The size can only be set or reset, not changed. */
72433d6423SLionel Sambuc 	if (size > 0 && trace_size > 0) return EBUSY;
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc 	/* Allocate or free a buffer for tracing data. For future multi-device
75433d6423SLionel Sambuc 	 * tracing support, the buffer is associated with a minor device.
76433d6423SLionel Sambuc 	 */
77433d6423SLionel Sambuc 	if (size == 0) {
78433d6423SLionel Sambuc 		if (trace_dev == NO_TRACEDEV) return OK;
79433d6423SLionel Sambuc 
80433d6423SLionel Sambuc 		if (trace_dev != minor) return EINVAL;
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc 		free(trace_buf);
83433d6423SLionel Sambuc 
84433d6423SLionel Sambuc 		trace_dev = NO_TRACEDEV;
85433d6423SLionel Sambuc 	} else {
86433d6423SLionel Sambuc 		if ((trace_buf = malloc(size * sizeof(btrace_entry))) == NULL)
87433d6423SLionel Sambuc 			return errno;
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc 		trace_dev = minor;
90433d6423SLionel Sambuc 	}
91433d6423SLionel Sambuc 
92433d6423SLionel Sambuc 	trace_size = size;
93433d6423SLionel Sambuc 	trace_pos = 0;
94433d6423SLionel Sambuc 	trace_next = 0;
95433d6423SLionel Sambuc 
96433d6423SLionel Sambuc 	return OK;
97433d6423SLionel Sambuc 
98433d6423SLionel Sambuc   case BIOCTRACECTL:
99433d6423SLionel Sambuc 	/* We can only start/stop tracing if the given device has a trace
100433d6423SLionel Sambuc 	 * buffer associated with it.
101433d6423SLionel Sambuc 	 */
102433d6423SLionel Sambuc 	if (trace_dev != minor) return EINVAL;
103433d6423SLionel Sambuc 
104433d6423SLionel Sambuc 	/* Copy in the request code. */
105433d6423SLionel Sambuc 	if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &ctl,
106433d6423SLionel Sambuc 		sizeof(ctl))) != OK)
107433d6423SLionel Sambuc 		return r;
108433d6423SLionel Sambuc 
109433d6423SLionel Sambuc 	/* Start or stop tracing. */
110433d6423SLionel Sambuc 	switch (ctl) {
111433d6423SLionel Sambuc 	case BTCTL_START:
112433d6423SLionel Sambuc 		if (trace_enabled) return EBUSY;
113433d6423SLionel Sambuc 
114433d6423SLionel Sambuc 		read_tsc_64(&trace_tsc);
115433d6423SLionel Sambuc 
116433d6423SLionel Sambuc 		trace_enabled = TRUE;
117433d6423SLionel Sambuc 
118433d6423SLionel Sambuc 		break;
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc 	case BTCTL_STOP:
121433d6423SLionel Sambuc 		if (!trace_enabled) return EINVAL;
122433d6423SLionel Sambuc 
123433d6423SLionel Sambuc 		trace_enabled = FALSE;
124433d6423SLionel Sambuc 
125433d6423SLionel Sambuc 		/* Cancel all ongoing trace operations. */
126433d6423SLionel Sambuc 		memset(trace_ptr, 0, sizeof(trace_ptr));
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc 		break;
129433d6423SLionel Sambuc 
130433d6423SLionel Sambuc 	default:
131433d6423SLionel Sambuc 		return EINVAL;
132433d6423SLionel Sambuc 	}
133433d6423SLionel Sambuc 
134433d6423SLionel Sambuc 	return OK;
135433d6423SLionel Sambuc 
136433d6423SLionel Sambuc   case BIOCTRACEGET:
137433d6423SLionel Sambuc 	/* We can only retrieve tracing entries if the given device has a trace
138433d6423SLionel Sambuc 	 * buffer associated with it.
139433d6423SLionel Sambuc 	 */
140433d6423SLionel Sambuc 	if (trace_dev != minor) return EINVAL;
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc 	if (trace_enabled) return EBUSY;
143433d6423SLionel Sambuc 
144433d6423SLionel Sambuc 	/* How much can we copy out? */
145433d6423SLionel Sambuc 	entries = MIN(trace_pos - trace_next,
146433d6423SLionel Sambuc 		_MINIX_IOCTL_SIZE_BIG(request) / sizeof(btrace_entry));
147433d6423SLionel Sambuc 
148433d6423SLionel Sambuc 	if (entries == 0)
149433d6423SLionel Sambuc 		return 0;
150433d6423SLionel Sambuc 
151433d6423SLionel Sambuc 	if ((r = sys_safecopyto(endpt, grant, 0,
152433d6423SLionel Sambuc 		(vir_bytes) &trace_buf[trace_next],
153433d6423SLionel Sambuc 		entries * sizeof(btrace_entry))) != OK)
154433d6423SLionel Sambuc 		return r;
155433d6423SLionel Sambuc 
156433d6423SLionel Sambuc 	trace_next += entries;
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc 	return entries;
159433d6423SLionel Sambuc   }
160433d6423SLionel Sambuc 
161433d6423SLionel Sambuc   return EINVAL;
162433d6423SLionel Sambuc }
163433d6423SLionel Sambuc 
164433d6423SLionel Sambuc /*===========================================================================*
165433d6423SLionel Sambuc  *				trace_start				     *
166433d6423SLionel Sambuc  *===========================================================================*/
trace_start(thread_id_t id,message * m_ptr)167433d6423SLionel Sambuc void trace_start(thread_id_t id, message *m_ptr)
168433d6423SLionel Sambuc {
169433d6423SLionel Sambuc /* Start creating a trace entry.
170433d6423SLionel Sambuc  */
171433d6423SLionel Sambuc   btrace_entry *entry;
172433d6423SLionel Sambuc   int req;
173433d6423SLionel Sambuc   u64_t pos;
174433d6423SLionel Sambuc   size_t size;
175433d6423SLionel Sambuc   int flags;
176433d6423SLionel Sambuc 
177433d6423SLionel Sambuc   if (!trace_enabled || trace_dev != m_ptr->m_lbdev_lblockdriver_msg.minor) return;
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc   assert(id >= 0 && id < MAX_THREADS + 1);
180433d6423SLionel Sambuc 
181433d6423SLionel Sambuc   if (trace_pos == trace_size)
182433d6423SLionel Sambuc 	return;
183433d6423SLionel Sambuc 
184433d6423SLionel Sambuc   switch (m_ptr->m_type) {
185433d6423SLionel Sambuc   case BDEV_OPEN:	req = BTREQ_OPEN;	break;
186433d6423SLionel Sambuc   case BDEV_CLOSE:	req = BTREQ_CLOSE;	break;
187433d6423SLionel Sambuc   case BDEV_READ:	req = BTREQ_READ;	break;
188433d6423SLionel Sambuc   case BDEV_WRITE:	req = BTREQ_WRITE;	break;
189433d6423SLionel Sambuc   case BDEV_GATHER:	req = BTREQ_GATHER;	break;
190433d6423SLionel Sambuc   case BDEV_SCATTER:	req = BTREQ_SCATTER;	break;
191433d6423SLionel Sambuc   case BDEV_IOCTL:	req = BTREQ_IOCTL;	break;
192433d6423SLionel Sambuc   default:		return;
193433d6423SLionel Sambuc   }
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc   switch (m_ptr->m_type) {
196433d6423SLionel Sambuc   case BDEV_OPEN:
197433d6423SLionel Sambuc   case BDEV_CLOSE:
198433d6423SLionel Sambuc 	pos = 0;
199433d6423SLionel Sambuc 	size = m_ptr->m_lbdev_lblockdriver_msg.access;
200433d6423SLionel Sambuc 	flags = 0;
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc 	break;
203433d6423SLionel Sambuc 
204433d6423SLionel Sambuc   case BDEV_READ:
205433d6423SLionel Sambuc   case BDEV_WRITE:
206433d6423SLionel Sambuc   case BDEV_GATHER:
207433d6423SLionel Sambuc   case BDEV_SCATTER:
208433d6423SLionel Sambuc 	pos = m_ptr->m_lbdev_lblockdriver_msg.pos;
209433d6423SLionel Sambuc 	size = m_ptr->m_lbdev_lblockdriver_msg.count;
210433d6423SLionel Sambuc 	flags = m_ptr->m_lbdev_lblockdriver_msg.flags;
211433d6423SLionel Sambuc 
212433d6423SLionel Sambuc 	break;
213433d6423SLionel Sambuc 
214433d6423SLionel Sambuc   case BDEV_IOCTL:
215433d6423SLionel Sambuc 	pos = 0;
216433d6423SLionel Sambuc 	size = m_ptr->m_lbdev_lblockdriver_msg.request;
217433d6423SLionel Sambuc 	flags = 0;
218433d6423SLionel Sambuc 
219433d6423SLionel Sambuc 	/* Do not log trace control requests. */
220433d6423SLionel Sambuc 	switch (size) {
221433d6423SLionel Sambuc 	case BIOCTRACEBUF:
222433d6423SLionel Sambuc 	case BIOCTRACECTL:
223433d6423SLionel Sambuc 	case BIOCTRACEGET:
224433d6423SLionel Sambuc 		return;
225433d6423SLionel Sambuc 	}
226433d6423SLionel Sambuc 
227433d6423SLionel Sambuc 	break;
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc   default:
230433d6423SLionel Sambuc 	/* Do not log any other messages. */
231433d6423SLionel Sambuc 	return;
232433d6423SLionel Sambuc   }
233433d6423SLionel Sambuc 
234433d6423SLionel Sambuc   entry = &trace_buf[trace_pos];
235433d6423SLionel Sambuc   entry->request = req;
236433d6423SLionel Sambuc   entry->size = size;
237433d6423SLionel Sambuc   entry->position = pos;
238433d6423SLionel Sambuc   entry->flags = flags;
239433d6423SLionel Sambuc   entry->result = BTRES_INPROGRESS;
240433d6423SLionel Sambuc   entry->start_time = trace_gettime();
241433d6423SLionel Sambuc   entry->finish_time = NO_TIME;
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc   trace_ptr[id] = entry;
244433d6423SLionel Sambuc   trace_pos++;
245433d6423SLionel Sambuc }
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc /*===========================================================================*
248433d6423SLionel Sambuc  *				trace_setsize				     *
249433d6423SLionel Sambuc  *===========================================================================*/
trace_setsize(thread_id_t id,size_t size)250433d6423SLionel Sambuc void trace_setsize(thread_id_t id, size_t size)
251433d6423SLionel Sambuc {
252433d6423SLionel Sambuc /* Set the current trace entry's actual (byte) size, for vector requests.
253433d6423SLionel Sambuc  */
254433d6423SLionel Sambuc   btrace_entry *entry;
255433d6423SLionel Sambuc 
256433d6423SLionel Sambuc   if (!trace_enabled) return;
257433d6423SLionel Sambuc 
258433d6423SLionel Sambuc   assert(id >= 0 && id < MAX_THREADS + 1);
259433d6423SLionel Sambuc 
260433d6423SLionel Sambuc   if ((entry = trace_ptr[id]) == NULL) return;
261433d6423SLionel Sambuc 
262433d6423SLionel Sambuc   entry->size = size;
263433d6423SLionel Sambuc }
264433d6423SLionel Sambuc 
265433d6423SLionel Sambuc /*===========================================================================*
266433d6423SLionel Sambuc  *				trace_finish				     *
267433d6423SLionel Sambuc  *===========================================================================*/
trace_finish(thread_id_t id,int result)268433d6423SLionel Sambuc void trace_finish(thread_id_t id, int result)
269433d6423SLionel Sambuc {
270433d6423SLionel Sambuc /* Finish a trace entry.
271433d6423SLionel Sambuc  */
272433d6423SLionel Sambuc   btrace_entry *entry;
273433d6423SLionel Sambuc 
274433d6423SLionel Sambuc   if (!trace_enabled) return;
275433d6423SLionel Sambuc 
276433d6423SLionel Sambuc   assert(id >= 0 && id < MAX_THREADS + 1);
277433d6423SLionel Sambuc 
278433d6423SLionel Sambuc   if ((entry = trace_ptr[id]) == NULL) return;
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc   entry->result = result;
281433d6423SLionel Sambuc   entry->finish_time = trace_gettime();
282433d6423SLionel Sambuc 
283433d6423SLionel Sambuc   trace_ptr[id] = NULL;
284433d6423SLionel Sambuc }
285