1 /* This file implements block level tracing support. */
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>
13 #define NO_TRACEDEV ((dev_t) -1)
14 #define NO_TIME ((u32_t) -1)
16 static int trace_enabled
= FALSE
;
17 static dev_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
;
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.
28 static btrace_entry
*trace_ptr
[MAX_THREADS
+ 1] = { NULL
};
30 /*===========================================================================*
32 *===========================================================================*/
33 static u32_t
trace_gettime(void)
35 /* Return the current time, in microseconds since the start of the trace.
39 assert(trace_enabled
);
45 return tsc_64_to_micros(tsc
);
48 /*===========================================================================*
50 *===========================================================================*/
51 int trace_ctl(dev_t minor
, unsigned long request
, endpoint_t endpt
,
54 /* Process a block trace control request.
61 /* The size cannot be changed when tracing is enabled. */
62 if (trace_enabled
) return EBUSY
;
64 /* Copy in the requested size. */
65 if ((r
= sys_safecopyfrom(endpt
, grant
, 0, (vir_bytes
) &size
,
69 if (size
>= INT_MAX
/ sizeof(btrace_entry
)) return EINVAL
;
71 /* The size can only be set or reset, not changed. */
72 if (size
> 0 && trace_size
> 0) return EBUSY
;
74 /* Allocate or free a buffer for tracing data. For future multi-device
75 * tracing support, the buffer is associated with a minor device.
78 if (trace_dev
== NO_TRACEDEV
) return OK
;
80 if (trace_dev
!= minor
) return EINVAL
;
84 trace_dev
= NO_TRACEDEV
;
86 if ((trace_buf
= malloc(size
* sizeof(btrace_entry
))) == NULL
)
99 /* We can only start/stop tracing if the given device has a trace
100 * buffer associated with it.
102 if (trace_dev
!= minor
) return EINVAL
;
104 /* Copy in the request code. */
105 if ((r
= sys_safecopyfrom(endpt
, grant
, 0, (vir_bytes
) &ctl
,
109 /* Start or stop tracing. */
112 if (trace_enabled
) return EBUSY
;
114 read_tsc_64(&trace_tsc
);
116 trace_enabled
= TRUE
;
121 if (!trace_enabled
) return EINVAL
;
123 trace_enabled
= FALSE
;
125 /* Cancel all ongoing trace operations. */
126 memset(trace_ptr
, 0, sizeof(trace_ptr
));
137 /* We can only retrieve tracing entries if the given device has a trace
138 * buffer associated with it.
140 if (trace_dev
!= minor
) return EINVAL
;
142 if (trace_enabled
) return EBUSY
;
144 /* How much can we copy out? */
145 entries
= MIN(trace_pos
- trace_next
,
146 _MINIX_IOCTL_SIZE_BIG(request
) / sizeof(btrace_entry
));
151 if ((r
= sys_safecopyto(endpt
, grant
, 0,
152 (vir_bytes
) &trace_buf
[trace_next
],
153 entries
* sizeof(btrace_entry
))) != OK
)
156 trace_next
+= entries
;
164 /*===========================================================================*
166 *===========================================================================*/
167 void trace_start(thread_id_t id
, message
*m_ptr
)
169 /* Start creating a trace entry.
177 if (!trace_enabled
|| trace_dev
!= m_ptr
->BDEV_MINOR
) return;
179 assert(id
>= 0 && id
< MAX_THREADS
+ 1);
181 if (trace_pos
== trace_size
)
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;
195 switch (m_ptr
->m_type
) {
199 size
= m_ptr
->BDEV_ACCESS
;
208 pos
= m_ptr
->BDEV_POS
;
209 size
= m_ptr
->BDEV_COUNT
;
210 flags
= m_ptr
->BDEV_FLAGS
;
216 size
= m_ptr
->BDEV_REQUEST
;
219 /* Do not log trace control requests. */
230 /* Do not log any other messages. */
234 entry
= &trace_buf
[trace_pos
];
235 entry
->request
= req
;
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
;
243 trace_ptr
[id
] = entry
;
247 /*===========================================================================*
249 *===========================================================================*/
250 void trace_setsize(thread_id_t id
, size_t size
)
252 /* Set the current trace entry's actual (byte) size, for vector requests.
256 if (!trace_enabled
) return;
258 assert(id
>= 0 && id
< MAX_THREADS
+ 1);
260 if ((entry
= trace_ptr
[id
]) == NULL
) return;
265 /*===========================================================================*
267 *===========================================================================*/
268 void trace_finish(thread_id_t id
, int result
)
270 /* Finish a trace entry.
274 if (!trace_enabled
) return;
276 assert(id
>= 0 && id
< MAX_THREADS
+ 1);
278 if ((entry
= trace_ptr
[id
]) == NULL
) return;
280 entry
->result
= result
;
281 entry
->finish_time
= trace_gettime();
283 trace_ptr
[id
] = NULL
;