]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/etap_macros.h
xnu-344.23.tar.gz
[apple/xnu.git] / osfmk / kern / etap_macros.h
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
de355530
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
1c79356b 11 *
de355530
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
de355530
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * @OSF_COPYRIGHT@
24 *
25 */
26/*
27 * The Event Trace Analysis Package
28 * ================================
29 *
30 * Function: Traces micro-kernel events.
31 *
32 * Macro Notes: Several macros are added throughout the lock code.
33 * These macros allow for convenient configuration
34 * and code readability.
35 *
36 * The macro prefixes determine a specific trace
37 * configuration operation:
38 *
39 * CUM - Cumulative trace specific operation.
40 * MON - Monitored trace specific operation.
41 * ETAP - Both a cumulative and monitored trace
42 * operation.
43 */
44
45
46#ifndef _KERN_ETAP_MACROS_H_
47#define _KERN_ETAP_MACROS_H_
48
49#include <kern/etap_options.h>
50#include <kern/lock.h>
51#include <mach/etap.h>
52#include <mach/etap_events.h>
53#include <kern/etap_pool.h>
54
55
56#if ETAP
57
1c79356b 58#include <mach/vm_param.h>
1c79356b
A
59#include <mach/message.h>
60
9bccf70c
A
61#include <kern/macro_help.h>
62
1c79356b
A
63extern void etap_init_phase1(void);
64extern void etap_init_phase2(void);
65extern void etap_event_table_assign(struct event_table_chain *, etap_event_t);
66extern unsigned int etap_get_pc(void);
67extern event_table_t event_table;
68extern subs_table_t subs_table;
69
70/*
71 * Time Macros
72 */
73
74#define ETAP_TIMESTAMP(t) rtc_gettime_interrupts_disabled(&t)
75#define ETAP_TIME_SUM(t,sum_me) t += sum_me
76#define ETAP_TIME_SUB(t,stop,start) \
77MACRO_BEGIN \
78 (t) = (stop); \
79 SUB_MACH_TIMESPEC(&(t), &(start)); \
80MACRO_END
81#define ETAP_TIME_SQR(t,sqr_me) t += sqr_me*sqr_me
82#define ETAP_TIME_DIV(r,n,d) r = (u_short) n/d
83#define ETAP_TIME_IS_ZERO(t) ((t).tv_sec == 0)
84#define ETAP_TIME_CLEAR(t) ((t).tv_sec = 0)
85#define ETAP_TIME_GREATER(t1,t2) ((t1) > (t2))
86
87#else /* ETAP */
88
89#define etap_init_phase1()
90#define etap_init_phase2()
91#define etap_event_table_assign(event)
92#define ETAP_TIMESTAMP(t)
93#define ETAP_TIME_SUB(t,start,stop)
94#define ETAP_TIME_CLEAR(t)
95
96#endif /* ETAP */
97
98
99/*
100 * ===================================================
101 * ETAP: cumulative trace specific macros
102 * ===================================================
103 */
104
105#if ETAP_LOCK_ACCUMULATE
106
107extern cbuff_entry_t etap_cbuff_reserve(event_table_t);
108#if MACH_LDEBUG
109extern simple_lock_t cbuff_locks;
110#else
111extern simple_lock_data_t cbuff_locks;
112#endif
113extern int cbuff_width;
114
115/*
116 * If cumulative hold tracing is enabled for the event (i.e., acquired lock),
117 * the CUM_HOLD_ACCUMULATE macro will update the appropriate cumulative buffer
118 * entry with the newly collected hold data.
119 */
120
121#define CUM_HOLD_ACCUMULATE(cp,total_time,dynamic,trace) \
122MACRO_BEGIN \
123 u_short _bucket; \
124 if ((cp) != CBUFF_ENTRY_NULL && ((trace) & CUM_DURATION)) { \
125 if (dynamic) \
126 simple_lock_no_trace(&cbuff_locks[dynamic-1]); \
127 (cp)->hold.triggered++; \
128 ETAP_TIME_SUM((cp)->hold.time,(total_time)); \
129 ETAP_TIME_SQR((cp)->hold.time_sq,(total_time)); \
130 if (ETAP_TIME_IS_ZERO((cp)->hold.min_time) || \
131 ETAP_TIME_GREATER((cp)->hold.min_time,(total_time))) \
132 (cp)->hold.min_time = (total_time); \
133 if (ETAP_TIME_GREATER((total_time),(cp)->hold.max_time)) \
134 (cp)->hold.max_time = (total_time); \
135 ETAP_TIME_DIV(_bucket,(total_time),cbuff_width); \
136 if (_bucket >= ETAP_CBUFF_IBUCKETS) \
137 (cp)->hold_interval[ETAP_CBUFF_IBUCKETS-1]++; \
138 else \
139 (cp)->hold_interval[_bucket]++; \
140 if (dynamic) \
141 simple_unlock_no_trace(&cbuff_locks[dynamic-1]); \
142 } \
143MACRO_END
144
145/*
146 * If cumulative wait tracing is enabled for the event (i.e., acquired lock),
147 * the CUM_WAIT_ACCUMULATE macro will update the appropriate cumulative
148 * buffer entry with the newly collected wait data.
149 */
150
151#define CUM_WAIT_ACCUMULATE(cp,total_time,dynamic,trace) \
152MACRO_BEGIN \
153 u_short _bucket; \
154 if ((cp) != CBUFF_ENTRY_NULL && ((trace) & CUM_CONTENTION)) { \
155 if (dynamic) \
156 simple_lock_no_trace(&cbuff_locks[dynamic-1]); \
157 (cp)->wait.triggered++; \
158 ETAP_TIME_SUM((cp)->wait.time,(total_time)); \
159 ETAP_TIME_SQR((cp)->wait.time_sq,(total_time)); \
160 if (ETAP_TIME_IS_ZERO((cp)->wait.min_time) || \
161 ETAP_TIME_GREATER((cp)->wait.min_time,(total_time))) \
162 (cp)->wait.min_time = (total_time); \
163 if (ETAP_TIME_GREATER((total_time),(cp)->wait.max_time)) \
164 (cp)->wait.max_time = (total_time); \
165 ETAP_TIME_DIV(_bucket,(total_time),cbuff_width); \
166 if (_bucket >= ETAP_CBUFF_IBUCKETS) \
167 (cp)->wait_interval[ETAP_CBUFF_IBUCKETS-1]++; \
168 else \
169 (cp)->wait_interval[_bucket]++; \
170 if (dynamic) \
171 simple_unlock_no_trace(&cbuff_locks[dynamic-1]); \
172 } \
173MACRO_END
174
175/*
176 * Initially a lock's cbuff_read pointer is set to CBUFF_ENTRY_NULL. This
177 * saves space in the cumulative buffer in the event that a read lock is
178 * not acquired. In the case that a read lock is acquired, the
179 * CUM_READ_ENTRY_RESERVE macro is called. Here a cumulative
180 * record is reserved and initialized.
181 */
182
183#define CUM_READ_ENTRY_RESERVE(l,cp,trace) \
184MACRO_BEGIN \
185 if ((cp) == CBUFF_ENTRY_NULL && (trace) & ETAP_CUMULATIVE) { \
186 (cp) = etap_cbuff_reserve(lock_event_table(l)); \
187 if ((cp) != CBUFF_ENTRY_NULL) { \
188 (cp)->event = lock_event_table(l)->event; \
189 (cp)->instance = (u_int) l; \
190 (cp)->kind = READ_LOCK; \
191 } \
192 } \
193MACRO_END
194
195#else /* ETAP_LOCK_ACCUMULATE */
196#define etap_cbuff_reserve(et)
197#define CUM_HOLD_ACCUMULATE(cp,t,d,tr)
198#define CUM_WAIT_ACCUMULATE(cp,t,d,tr)
199#define CUM_READ_ENTRY_RESERVE(l,rep,tr)
200#endif /* ETAP_LOCK_ACCUMULATE */
201
202/*
203 * ===============================================
204 * ETAP: monitor trace specific macros
205 * ===============================================
206 */
207
208#if ETAP_MONITOR
209extern int mbuff_entries;
210extern monitor_buffer_t mbuff[];
211#endif /* ETAP_MONITOR */
212
213
214#if ETAP_LOCK_MONITOR
215
216/*
217 * If monitor tracing is enabled for the lock, the
218 * MON_DATA_COLLECT macro will write collected lock data to
219 * the next slot in a cpu specific monitor buffer. Circular
220 * buffer maintenance is also performed here.
221 */
222
223#define MON_DATA_COLLECT(l,e,total_time,type,op,trace) \
224MACRO_BEGIN \
225 mbuff_entry_t _mp; \
226 int _cpu, _ent, _s; \
227 if ((trace) & op) { \
228 mp_disable_preemption(); \
229 _cpu = cpu_number(); \
230 _s = splhigh(); \
231 _ent = mbuff[_cpu]->free; \
232 _mp = &mbuff[_cpu]->entry[_ent]; \
233 _mp->event = lock_event_table(l)->event; \
234 _mp->flags = ((op) | (type)); \
235 _mp->instance = (u_int) (l); \
236 _mp->time = (total_time); \
237 _mp->data[0] = (e)->start_pc; \
238 _mp->data[1] = (e)->end_pc; \
239 mbuff[_cpu]->free = (_ent+1) % mbuff_entries; \
240 if (mbuff[_cpu]->free == 0) \
241 mbuff[_cpu]->timestamp++; \
242 splx(_s); \
243 mp_enable_preemption(); \
244 } \
245MACRO_END
246
247#define MON_CLEAR_PCS(l) \
248MACRO_BEGIN \
249 (l)->start_pc = 0; \
250 (l)->end_pc = 0; \
251MACRO_END
252
253#define MON_ASSIGN_PC(target,source,trace) \
254 if ((trace) & ETAP_MONITORED) target = source
255
256#else /* ETAP_LOCK_MONITOR */
257#define MON_DATA_COLLECT(l,le,tt,t,o,tr)
258#define MON_GET_PC(pc,tr)
259#define MON_CLEAR_PCS(l)
260#define MON_ASSIGN_PC(t,s,tr)
261#endif /* ETAP_LOCK_MONITOR */
262
263
9bccf70c
A
264#if ETAP_EVENT_MONITOR
265
266#include <mach/exception_types.h>
267
268#define ETAP_EXCEPTION_PROBE(_f, _th, _ex, _sysnum) \
269 if (_ex == EXC_SYSCALL) { \
270 ETAP_PROBE_DATA(ETAP_P_SYSCALL_UNIX, \
271 _f, \
272 _th, \
273 _sysnum, \
274 sizeof(int)); \
275 }
276#else /* ETAP_EVENT_MONITOR */
277#define ETAP_EXCEPTION_PROBE(_f, _th, _ex, _sysnum)
278#endif /* ETAP_EVENT_MONITOR */
279
1c79356b
A
280#if ETAP_EVENT_MONITOR
281
282#define ETAP_PROBE_DATA_COND(_event, _flags, _thread, _data, _size, _cond) \
283MACRO_BEGIN \
284 mbuff_entry_t _mp; \
285 int _cpu, _ent, _s; \
286 if (event_table[_event].status && (_cond)) { \
287 mp_disable_preemption(); \
288 _cpu = cpu_number(); \
289 _s = splhigh(); \
290 _ent = mbuff[_cpu]->free; \
291 _mp = &mbuff[_cpu]->entry[_ent]; \
292 ETAP_TIMESTAMP(_mp->time); \
293 _mp->pc = etap_get_pc(); \
294 _mp->event = _event; \
295 _mp->flags = KERNEL_EVENT | _flags; \
296 _mp->instance = (u_int) _thread; \
297 bcopy((char *) _data, (char *) _mp->data, _size); \
298 mbuff[_cpu]->free = (_ent+1) % mbuff_entries; \
299 if (mbuff[_cpu]->free == 0) \
300 mbuff[_cpu]->timestamp++; \
301 splx(_s); \
302 mp_enable_preemption(); \
303 } \
304MACRO_END
305
306#define ETAP_PROBE(_event, _flags, _thread) \
307 ETAP_PROBE_DATA_COND(_event, _flags, _thread, 0, 0, 1)
308
309#define ETAP_PROBE_DATA(_event, _flags, _thread, _data, _size) \
310 ETAP_PROBE_DATA_COND(_event, _flags, _thread, _data, _size, \
311 (_thread)->etap_trace)
312
313#define ETAP_DATA_LOAD(ed, x) ((ed) = (u_int) (x))
314#define ETAP_SET_REASON(_th, _reason) ((_th)->etap_reason = (_reason))
315
316#else /* ETAP_EVENT_MONITOR */
317#define ETAP_PROBE(e,f,th)
318#define ETAP_PROBE_DATA(e,f,th,d,s)
319#define ETAP_PROBE_DATA_COND(e,f,th,d,s,c)
320#define ETAP_DATA_LOAD(d,x);
321#define ETAP_SET_REASON(t,r)
322#endif /* ETAP_EVENT_MONITOR */
323
324/*
325 * =================================
326 * ETAP: general lock macros
327 * =================================
328 */
329
330#if ETAP_LOCK_TRACE
331
332#define ETAP_TOTAL_TIME(t,stop,start) \
333 ETAP_TIME_SUB((t),(stop),(start))
334
335#define ETAP_DURATION_TIMESTAMP(e,trace) \
336MACRO_BEGIN \
337 if ((trace) & ETAP_DURATION) \
338 ETAP_TIMESTAMP((e)->start_hold_time); \
339MACRO_END
340
341#define ETAP_COPY_START_HOLD_TIME(entry,time,trace) \
342MACRO_BEGIN \
343 if ((trace) & ETAP_DURATION) \
344 (entry)->start_hold_time = time; \
345MACRO_END
346
347#define ETAP_CONTENTION_TIMESTAMP(e,trace) \
348MACRO_BEGIN \
349 if ((trace) & ETAP_CONTENTION) \
350 ETAP_TIMESTAMP((e)->start_wait_time); \
351MACRO_END
352
353#define ETAP_STAMP(event_table,trace,dynamic) \
354MACRO_BEGIN \
355 if ((event_table) != EVENT_TABLE_NULL) { \
356 (dynamic) = (event_table)->dynamic; \
357 (trace) = (event_table)->status; \
358 } \
359MACRO_END
360
361#define ETAP_WHOLE_OP(l) \
362 (!(ETAP_TIME_IS_ZERO((l)->u.s.start_hold_time)))
363#define ETAP_DURATION_ENABLED(trace) ((trace) & ETAP_DURATION)
364#define ETAP_CONTENTION_ENABLED(trace) ((trace) & ETAP_CONTENTION)
365
366/*
367 * The ETAP_CLEAR_TRACE_DATA macro sets the etap specific fields
368 * of the simple_lock_t structure to zero.
369 *
370 * This is always done just before a simple lock is released.
371 */
372
373#define ETAP_CLEAR_TRACE_DATA(l) \
374MACRO_BEGIN \
375 ETAP_TIME_CLEAR((l)->u.s.start_hold_time); \
376 MON_CLEAR_PCS((l)); \
377MACRO_END
378
379
380/* ==================================================
381 * The ETAP_XXX_ENTRY macros manipulate the locks
382 * start_list (a linked list of start data).
383 * ==================================================
384 */
385
386#define ETAP_CREATE_ENTRY(entry,trace) \
387MACRO_BEGIN \
388 if ((trace) & ETAP_TRACE_ON) \
389 (entry) = get_start_data_node(); \
390MACRO_END
391
392#define ETAP_LINK_ENTRY(l,entry,trace) \
393MACRO_BEGIN \
394 if ((trace) & ETAP_TRACE_ON) { \
395 (entry)->next = (l)->u.s.start_list; \
396 (l)->u.s.start_list = (entry); \
397 (entry)->thread_id = (u_int) current_thread(); \
398 ETAP_TIME_CLEAR((entry)->start_wait_time); \
399 } \
400MACRO_END
401
402#define ETAP_FIND_ENTRY(l,entry,trace) \
403MACRO_BEGIN \
404 u_int _ct; \
405 _ct = (u_int) current_thread(); \
406 (entry) = (l)->u.s.start_list; \
407 while ((entry) != SD_ENTRY_NULL && (entry)->thread_id != _ct) \
408 (entry) = (entry)->next; \
409 if ((entry) == SD_ENTRY_NULL) \
410 (trace) = 0; \
411MACRO_END
412
413#define ETAP_UNLINK_ENTRY(l,entry) \
414MACRO_BEGIN \
415 boolean_t _first = TRUE; \
416 start_data_node_t _prev; \
417 u_int _ct; \
418 _ct = (u_int) current_thread(); \
419 (entry) = (l)->u.s.start_list; \
420 while ((entry) != SD_ENTRY_NULL && (entry)->thread_id != _ct){ \
421 _prev = (entry); \
422 (entry) = (entry)->next; \
423 _first = FALSE; \
424 } \
425 if (entry != SD_ENTRY_NULL) { \
426 if (_first) \
427 (l)->u.s.start_list = (entry)->next; \
428 else \
429 _prev->next = (entry)->next; \
430 (entry)->next = SD_ENTRY_NULL; \
431 } \
432MACRO_END
433
434#define ETAP_DESTROY_ENTRY(entry) \
435MACRO_BEGIN \
436 if ((entry) != SD_ENTRY_NULL) \
437 free_start_data_node ((entry)); \
438MACRO_END
439
440#else /* ETAP_LOCK_TRACE */
441#define ETAP_TOTAL_TIME(t,stop,start)
442#define ETAP_DURATION_TIMESTAMP(le,tr)
443#define ETAP_CONTENTION_TIMESTAMP(le,tr)
444#define ETAP_COPY_START_HOLD_TIME(le,t,tr)
445#define ETAP_STAMP(tt,tr,d)
446#define ETAP_DURATION_ENABLED(tr) (0) /* always fails */
447#define ETAP_CONTENTION_ENABLED(tr) (0) /* always fails */
448#define ETAP_CLEAR_TRACE_DATA(l)
449#define ETAP_CREATE_ENTRY(e,tr)
450#define ETAP_LINK_ENTRY(l,e,tr)
451#define ETAP_FIND_ENTRY(l,e,tr)
452#define ETAP_UNLINK_ENTRY(l,e)
453#define ETAP_DESTROY_ENTRY(e)
454#endif /* ETAP_LOCK_TRACE */
455
456#endif /* _KERN_ETAP_MACROS_H_ */