debuggers.hg

annotate xen/common/trace.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents f5f3cf4e001f
children
rev   line source
mwilli2@1151 1 /******************************************************************************
mwilli2@1151 2 * common/trace.c
mwilli2@1151 3 *
mwilli2@1151 4 * Xen Trace Buffer
mwilli2@1151 5 *
mwilli2@1151 6 * Copyright (C) 2004 by Intel Research Cambridge
mwilli2@1151 7 *
kaf24@7604 8 * Authors: Mark Williamson, mark.a.williamson@intel.com
kaf24@7604 9 * Rob Gardner, rob.gardner@hp.com
kaf24@7604 10 * Date: October 2005
mwilli2@1151 11 *
bren@4534 12 * Copyright (C) 2005 Bin Ren
bren@4534 13 *
mwilli2@1151 14 * The trace buffer code is designed to allow debugging traces of Xen to be
mwilli2@1151 15 * generated on UP / SMP machines. Each trace entry is timestamped so that
mwilli2@1151 16 * it's possible to reconstruct a chronological record of trace events.
kaf24@1154 17 */
mwilli2@1151 18
kaf24@1248 19 #include <xen/config.h>
mwilli2@1151 20 #include <asm/types.h>
mwilli2@1151 21 #include <asm/io.h>
kaf24@1248 22 #include <xen/lib.h>
kaf24@1248 23 #include <xen/sched.h>
kaf24@1248 24 #include <xen/smp.h>
kaf24@1248 25 #include <xen/trace.h>
kaf24@1248 26 #include <xen/errno.h>
kaf24@9625 27 #include <xen/event.h>
keir@21242 28 #include <xen/tasklet.h>
smh22@3405 29 #include <xen/init.h>
kaf24@11236 30 #include <xen/mm.h>
kfraser@10728 31 #include <xen/percpu.h>
keir@21435 32 #include <xen/cpu.h>
mwilli2@1151 33 #include <asm/atomic.h>
kfraser@11295 34 #include <public/sysctl.h>
mwilli2@1151 35
ack@13311 36 #ifdef CONFIG_COMPAT
ack@13311 37 #include <compat/trace.h>
ack@13311 38 #define xen_t_buf t_buf
ack@13311 39 CHECK_t_buf;
ack@13311 40 #undef xen_t_buf
ack@13311 41 #else
ack@13311 42 #define compat_t_rec t_rec
ack@13311 43 #endif
ack@13311 44
kaf24@3372 45 /* opt_tbuf_size: trace buffer size (in pages) */
kaf24@7604 46 static unsigned int opt_tbuf_size = 0;
kaf24@3372 47 integer_param("tbuf_size", opt_tbuf_size);
kaf24@3372 48
mwilli2@1151 49 /* Pointers to the meta-data objects for all system trace buffers */
keir@20873 50 static struct t_info *t_info;
keir@20873 51 #define T_INFO_PAGES 2 /* Size fixed at 2 pages for now. */
keir@21197 52 #define T_INFO_SIZE ((T_INFO_PAGES)*(PAGE_SIZE))
keir@19964 53 static DEFINE_PER_CPU_READ_MOSTLY(struct t_buf *, t_bufs);
keir@19964 54 static DEFINE_PER_CPU_READ_MOSTLY(unsigned char *, t_data);
keir@20873 55 static DEFINE_PER_CPU_READ_MOSTLY(spinlock_t, t_lock);
keir@21752 56 static u32 data_size;
keir@21749 57 static u32 t_info_first_offset __read_mostly;
mwilli2@1151 58
kaf24@9625 59 /* High water mark for trace buffers; */
kaf24@9625 60 /* Send virtual interrupt when buffer level reaches this point */
keir@21752 61 static u32 t_buf_highwater;
kaf24@9625 62
kfraser@10698 63 /* Number of records lost due to per-CPU trace buffer being full. */
kfraser@10698 64 static DEFINE_PER_CPU(unsigned long, lost_records);
keir@18476 65 static DEFINE_PER_CPU(unsigned long, lost_records_first_tsc);
kaf24@9625 66
kaf24@7604 67 /* a flag recording whether initialization has been done */
kaf24@7604 68 /* or more properly, if the tbuf subsystem is enabled right now */
kfraser@14174 69 int tb_init_done __read_mostly;
mwilli2@1151 70
bren@4534 71 /* which CPUs tracing is enabled on */
kfraser@11295 72 static cpumask_t tb_cpu_mask = CPU_MASK_ALL;
bren@4534 73
bren@4534 74 /* which tracing events are enabled */
kaf24@7606 75 static u32 tb_event_mask = TRC_ALL;
kaf24@7604 76
keir@21749 77 /* Return the number of elements _type necessary to store at least _x bytes of data
keir@21749 78 * i.e., sizeof(_type) * ans >= _x. */
keir@21749 79 #define fit_to_type(_type, _x) (((_x)+sizeof(_type)-1) / sizeof(_type))
keir@21749 80
keir@21749 81 static void calc_tinfo_first_offset(void)
keir@21749 82 {
keir@22613 83 int offset_in_bytes = offsetof(struct t_info, mfn_offset[NR_CPUS]);
keir@21749 84 t_info_first_offset = fit_to_type(uint32_t, offset_in_bytes);
keir@21749 85 }
keir@21749 86
kaf24@7604 87 /**
keir@21197 88 * check_tbuf_size - check to make sure that the proposed size will fit
keir@21750 89 * in the currently sized struct t_info and allows prod and cons to
keir@21750 90 * reach double the value without overflow.
keir@21197 91 */
keir@21750 92 static int check_tbuf_size(u32 pages)
keir@21197 93 {
keir@21750 94 struct t_buf dummy;
keir@21750 95 typeof(dummy.prod) size;
keir@21750 96
keir@21750 97 size = ((typeof(dummy.prod))pages) * PAGE_SIZE;
keir@21750 98
keir@21750 99 return (size / PAGE_SIZE != pages)
keir@21750 100 || (size + size < size)
keir@21750 101 || (num_online_cpus() * pages + t_info_first_offset > T_INFO_SIZE / sizeof(uint32_t));
keir@21197 102 }
keir@21197 103
keir@21197 104 /**
kaf24@7604 105 * alloc_trace_bufs - performs initialization of the per-cpu trace buffers.
kaf24@7604 106 *
kaf24@7604 107 * This function is called at start of day in order to initialize the per-cpu
kaf24@7604 108 * trace buffers. The trace buffers are then available for debugging use, via
kaf24@7604 109 * the %TRACE_xD macros exported in <xen/trace.h>.
kaf24@7604 110 *
kaf24@7604 111 * This function may also be called later when enabling trace buffers
kaf24@7604 112 * via the SET_SIZE hypercall.
kaf24@7604 113 */
kaf24@7606 114 static int alloc_trace_bufs(void)
kaf24@7604 115 {
keir@20873 116 int i, cpu, order;
kaf24@7604 117 unsigned long nr_pages;
keir@20873 118 /* Start after a fixed-size array of NR_CPUS */
keir@22613 119 uint32_t *t_info_mfn_list;
keir@22613 120 int offset;
kaf24@7604 121
kaf24@7604 122 if ( opt_tbuf_size == 0 )
kaf24@7604 123 return -EINVAL;
kaf24@7604 124
keir@22613 125 if ( check_tbuf_size(opt_tbuf_size) )
kaf24@1154 126 {
keir@22613 127 printk("Xen trace buffers: tb size %d too large. "
keir@22613 128 "Tracing disabled.\n",
keir@22613 129 opt_tbuf_size);
kaf24@7604 130 return -EINVAL;
mwilli2@1151 131 }
mwilli2@1182 132
keir@22613 133 /* t_info size is fixed for now. Currently this works great, so there
keir@22613 134 * seems to be no need to make it dynamic. */
keir@22613 135 t_info = alloc_xenheap_pages(get_order_from_pages(T_INFO_PAGES), 0);
keir@22613 136 if ( t_info == NULL )
keir@22613 137 {
keir@22613 138 printk("Xen trace buffers: t_info allocation failed! "
keir@22613 139 "Tracing disabled.\n");
keir@22613 140 return -ENOMEM;
keir@22613 141 }
keir@22613 142
keir@22613 143 for ( i = 0; i < T_INFO_PAGES; i++ )
keir@22613 144 share_xen_page_with_privileged_guests(
keir@22613 145 virt_to_page(t_info) + i, XENSHARE_readonly);
keir@22613 146
keir@22613 147 t_info_mfn_list = (uint32_t *)t_info;
keir@22613 148 offset = t_info_first_offset;
keir@22613 149
keir@20873 150 t_info->tbuf_size = opt_tbuf_size;
keir@21751 151 printk(XENLOG_INFO "tbuf_size %d\n", t_info->tbuf_size);
keir@20873 152
keir@20873 153 nr_pages = opt_tbuf_size;
keir@20873 154 order = get_order_from_pages(nr_pages);
keir@20873 155
keir@20873 156 /*
keir@20873 157 * First, allocate buffers for all of the cpus. If any
keir@20873 158 * fails, deallocate what you have so far and exit.
keir@20873 159 */
keir@20873 160 for_each_online_cpu(cpu)
keir@20873 161 {
keir@20873 162 int flags;
keir@20873 163 char *rawbuf;
keir@20873 164 struct t_buf *buf;
kaf24@9214 165
keir@21728 166 if ( (rawbuf = alloc_xenheap_pages(
keir@21728 167 order, MEMF_bits(32 + PAGE_SHIFT))) == NULL )
keir@20873 168 {
keir@20873 169 printk("Xen trace buffers: memory allocation failed\n");
keir@20873 170 opt_tbuf_size = 0;
keir@20873 171 goto out_dealloc;
keir@20873 172 }
keir@20873 173
keir@20873 174 spin_lock_irqsave(&per_cpu(t_lock, cpu), flags);
keir@20873 175
keir@21752 176 per_cpu(t_bufs, cpu) = buf = (struct t_buf *)rawbuf;
kaf24@7609 177 buf->cons = buf->prod = 0;
keir@20873 178 per_cpu(t_data, cpu) = (unsigned char *)(buf + 1);
keir@20873 179
keir@20873 180 spin_unlock_irqrestore(&per_cpu(t_lock, cpu), flags);
keir@20873 181
mwilli2@1151 182 }
kaf24@7609 183
keir@20873 184 /*
keir@20873 185 * Now share the pages to xentrace can map them, and write them in
keir@20873 186 * the global t_info structure.
keir@20873 187 */
keir@20873 188 for_each_online_cpu(cpu)
keir@20873 189 {
keir@20873 190 /* Share pages so that xentrace can map them. */
keir@20873 191 char *rawbuf;
keir@20873 192
keir@20873 193 if ( (rawbuf = (char *)per_cpu(t_bufs, cpu)) )
keir@20873 194 {
keir@20873 195 struct page_info *p = virt_to_page(rawbuf);
keir@20873 196 uint32_t mfn = virt_to_mfn(rawbuf);
keir@20873 197
keir@20873 198 for ( i = 0; i < nr_pages; i++ )
keir@20873 199 {
keir@20873 200 share_xen_page_with_privileged_guests(
keir@20873 201 p + i, XENSHARE_writable);
keir@20873 202
keir@20873 203 t_info_mfn_list[offset + i]=mfn + i;
keir@20873 204 }
keir@20873 205 /* Write list first, then write per-cpu offset. */
keir@20873 206 wmb();
keir@20873 207 t_info->mfn_offset[cpu]=offset;
keir@21751 208 printk(XENLOG_INFO "p%d mfn %"PRIx32" offset %d\n",
keir@20873 209 cpu, mfn, offset);
keir@20873 210 offset+=i;
keir@20873 211 }
keir@20873 212 }
keir@20873 213
keir@20873 214 data_size = (opt_tbuf_size * PAGE_SIZE - sizeof(struct t_buf));
gdunlap@15968 215 t_buf_highwater = data_size >> 1; /* 50% high water */
kaf24@9625 216
kaf24@7604 217 return 0;
keir@20873 218 out_dealloc:
keir@20873 219 for_each_online_cpu(cpu)
keir@20873 220 {
keir@20873 221 int flags;
keir@20873 222 char * rawbuf;
keir@20873 223
keir@20873 224 spin_lock_irqsave(&per_cpu(t_lock, cpu), flags);
keir@20873 225 if ( (rawbuf = (char *)per_cpu(t_bufs, cpu)) )
keir@20873 226 {
keir@21752 227 per_cpu(t_bufs, cpu) = NULL;
keir@20873 228 ASSERT(!(virt_to_page(rawbuf)->count_info & PGC_allocated));
keir@20873 229 free_xenheap_pages(rawbuf, order);
keir@20873 230 }
keir@20873 231 spin_unlock_irqrestore(&per_cpu(t_lock, cpu), flags);
keir@20873 232 }
keir@21197 233
keir@21197 234 return -ENOMEM;
kaf24@7604 235 }
mwilli2@1151 236
kaf24@7604 237
kaf24@7604 238 /**
kaf24@7604 239 * tb_set_size - handle the logic involved with dynamically
kaf24@7604 240 * allocating and deallocating tbufs
kaf24@7604 241 *
kaf24@7604 242 * This function is called when the SET_SIZE hypercall is done.
kaf24@7604 243 */
kaf24@7606 244 static int tb_set_size(int size)
kaf24@7604 245 {
kaf24@7606 246 /*
kaf24@7606 247 * Setting size is a one-shot operation. It can be done either at
kaf24@7606 248 * boot time or via control tools, but not by both. Once buffers
kaf24@7606 249 * are created they cannot be destroyed.
kaf24@7606 250 */
keir@21197 251 int ret = 0;
keir@21197 252
keir@22613 253 if ( opt_tbuf_size != 0 )
kaf24@7606 254 {
keir@21197 255 if ( size != opt_tbuf_size )
keir@21197 256 gdprintk(XENLOG_INFO, "tb_set_size from %d to %d not implemented\n",
keir@21197 257 opt_tbuf_size, size);
keir@21197 258 return -EINVAL;
keir@21197 259 }
keir@21197 260
keir@21197 261 if ( size <= 0 )
keir@21197 262 return -EINVAL;
keir@21197 263
keir@22613 264 opt_tbuf_size = size;
keir@22613 265
keir@22613 266 if ( (ret = alloc_trace_bufs()) != 0 )
keir@21197 267 {
keir@22613 268 opt_tbuf_size = 0;
keir@22613 269 return ret;
kaf24@7606 270 }
kaf24@7606 271
keir@22613 272 printk("Xen trace buffers: initialized\n");
keir@22613 273 return 0;
kaf24@7606 274 }
kaf24@7606 275
keir@18479 276 int trace_will_trace_event(u32 event)
keir@18479 277 {
keir@18479 278 if ( !tb_init_done )
keir@18479 279 return 0;
keir@18479 280
keir@18479 281 /*
keir@18479 282 * Copied from __trace_var()
keir@18479 283 */
keir@18479 284 if ( (tb_event_mask & event) == 0 )
keir@18479 285 return 0;
keir@18479 286
keir@18479 287 /* match class */
keir@18479 288 if ( ((tb_event_mask >> TRC_CLS_SHIFT) & (event >> TRC_CLS_SHIFT)) == 0 )
keir@18479 289 return 0;
keir@18479 290
keir@18479 291 /* then match subclass */
keir@18479 292 if ( (((tb_event_mask >> TRC_SUBCLS_SHIFT) & 0xf )
keir@18479 293 & ((event >> TRC_SUBCLS_SHIFT) & 0xf )) == 0 )
keir@18479 294 return 0;
keir@18479 295
keir@18479 296 if ( !cpu_isset(smp_processor_id(), tb_cpu_mask) )
keir@18479 297 return 0;
keir@18479 298
keir@18479 299 return 1;
keir@18479 300 }
kaf24@7606 301
keir@21435 302 static int cpu_callback(
keir@21435 303 struct notifier_block *nfb, unsigned long action, void *hcpu)
keir@21435 304 {
keir@21435 305 unsigned int cpu = (unsigned long)hcpu;
keir@21435 306
keir@21435 307 if ( action == CPU_UP_PREPARE )
keir@21435 308 spin_lock_init(&per_cpu(t_lock, cpu));
keir@21435 309
keir@21435 310 return NOTIFY_DONE;
keir@21435 311 }
keir@21435 312
keir@21435 313 static struct notifier_block cpu_nfb = {
keir@21435 314 .notifier_call = cpu_callback
keir@21435 315 };
keir@22613 316
kaf24@7606 317 /**
kaf24@7606 318 * init_trace_bufs - performs initialization of the per-cpu trace buffers.
kaf24@7606 319 *
kaf24@7606 320 * This function is called at start of day in order to initialize the per-cpu
kaf24@7606 321 * trace buffers. The trace buffers are then available for debugging use, via
kaf24@7606 322 * the %TRACE_xD macros exported in <xen/trace.h>.
kaf24@7606 323 */
keir@15081 324 void __init init_trace_bufs(void)
kaf24@7606 325 {
keir@20873 326 int i;
keir@21749 327
keir@21749 328 /* Calculate offset in u32 of first mfn */
keir@21749 329 calc_tinfo_first_offset();
keir@21749 330
keir@22613 331 /* Per-cpu t_lock initialisation. */
keir@21435 332 for_each_online_cpu ( i )
keir@20873 333 spin_lock_init(&per_cpu(t_lock, i));
keir@21435 334 register_cpu_notifier(&cpu_nfb);
keir@20873 335
kaf24@7606 336 if ( opt_tbuf_size == 0 )
kaf24@7606 337 {
kaf24@7606 338 printk("Xen trace buffers: disabled\n");
keir@22613 339 goto fail;
keir@21197 340 }
kaf24@7606 341
keir@22613 342 if ( alloc_trace_bufs() != 0 )
kaf24@7606 343 {
keir@22613 344 dprintk(XENLOG_INFO, "Xen trace buffers: "
keir@22613 345 "allocation size %d failed, disabling\n",
keir@22613 346 opt_tbuf_size);
keir@22613 347 goto fail;
kaf24@7604 348 }
keir@22613 349
keir@22613 350 printk("Xen trace buffers: initialised\n");
keir@22613 351 wmb(); /* above must be visible before tb_init_done flag set */
keir@22613 352 tb_init_done = 1;
keir@22613 353 return;
keir@22613 354
keir@22613 355 fail:
keir@22613 356 opt_tbuf_size = 0;
kaf24@7604 357 }
mwilli2@1151 358
mwilli2@1151 359 /**
kfraser@11295 360 * tb_control - sysctl operations on trace buffers.
kfraser@11295 361 * @tbc: a pointer to a xen_sysctl_tbuf_op_t to be filled out
mwilli2@1151 362 */
kfraser@11295 363 int tb_control(xen_sysctl_tbuf_op_t *tbc)
mwilli2@1151 364 {
kaf24@10288 365 static DEFINE_SPINLOCK(lock);
bren@4534 366 int rc = 0;
bren@4534 367
bren@4534 368 spin_lock(&lock);
bren@4534 369
kfraser@11295 370 switch ( tbc->cmd )
mwilli2@1163 371 {
kfraser@11295 372 case XEN_SYSCTL_TBUFOP_get_info:
kaf24@6319 373 tbc->evt_mask = tb_event_mask;
keir@20873 374 tbc->buffer_mfn = t_info ? virt_to_mfn(t_info) : 0;
keir@21379 375 tbc->size = T_INFO_PAGES * PAGE_SIZE;
bren@4534 376 break;
kfraser@11295 377 case XEN_SYSCTL_TBUFOP_set_cpu_mask:
keir@21396 378 rc = xenctl_cpumap_to_cpumask(&tb_cpu_mask, &tbc->cpu_mask);
bren@4534 379 break;
kfraser@11295 380 case XEN_SYSCTL_TBUFOP_set_evt_mask:
bren@4534 381 tb_event_mask = tbc->evt_mask;
bren@4534 382 break;
kfraser@11295 383 case XEN_SYSCTL_TBUFOP_set_size:
keir@20873 384 rc = tb_set_size(tbc->size);
kaf24@7604 385 break;
kfraser@11295 386 case XEN_SYSCTL_TBUFOP_enable:
kaf24@7606 387 /* Enable trace buffers. Check buffers are already allocated. */
kaf24@7606 388 if ( opt_tbuf_size == 0 )
kaf24@7604 389 rc = -EINVAL;
kaf24@7604 390 else
kaf24@7604 391 tb_init_done = 1;
kaf24@7604 392 break;
kfraser@11295 393 case XEN_SYSCTL_TBUFOP_disable:
keir@20925 394 {
kaf24@7606 395 /*
kaf24@7606 396 * Disable trace buffers. Just stops new records from being written,
kaf24@7606 397 * does not deallocate any memory.
kaf24@7606 398 */
keir@20925 399 int i;
keir@20925 400
kaf24@7604 401 tb_init_done = 0;
keir@20925 402 wmb();
keir@20925 403 /* Clear any lost-record info so we don't get phantom lost records next time we
keir@20925 404 * start tracing. Grab the lock to make sure we're not racing anyone. After this
keir@20925 405 * hypercall returns, no more records should be placed into the buffers. */
keir@20925 406 for_each_online_cpu(i)
keir@20925 407 {
keir@20925 408 int flags;
keir@20925 409 spin_lock_irqsave(&per_cpu(t_lock, i), flags);
keir@20925 410 per_cpu(lost_records, i)=0;
keir@20925 411 spin_unlock_irqrestore(&per_cpu(t_lock, i), flags);
keir@20925 412 }
keir@20925 413 }
kaf24@7604 414 break;
bren@4534 415 default:
bren@4534 416 rc = -EINVAL;
kaf24@7606 417 break;
mwilli2@1163 418 }
bren@4534 419
bren@4534 420 spin_unlock(&lock);
bren@4534 421
bren@4534 422 return rc;
mwilli2@1151 423 }
kaf24@3952 424
keir@21752 425 static inline unsigned int calc_rec_size(bool_t cycles, unsigned int extra)
gdunlap@15968 426 {
keir@21752 427 unsigned int rec_size = 4;
keir@21752 428
keir@15979 429 if ( cycles )
gdunlap@15968 430 rec_size += 8;
gdunlap@15968 431 rec_size += extra;
gdunlap@15968 432 return rec_size;
gdunlap@15968 433 }
gdunlap@15968 434
keir@21752 435 static inline bool_t bogus(u32 prod, u32 cons)
keir@17038 436 {
keir@21752 437 if ( unlikely(prod & 3) || unlikely(prod >= 2 * data_size) ||
keir@21752 438 unlikely(cons & 3) || unlikely(cons >= 2 * data_size) )
keir@21752 439 {
keir@21752 440 tb_init_done = 0;
keir@21752 441 printk(XENLOG_WARNING "trc#%u: bogus prod (%08x) and/or cons (%08x)\n",
keir@21752 442 smp_processor_id(), prod, cons);
keir@21752 443 return 1;
keir@21752 444 }
keir@21752 445 return 0;
keir@21752 446 }
keir@21752 447
keir@21752 448 static inline u32 calc_unconsumed_bytes(const struct t_buf *buf)
keir@21752 449 {
keir@21752 450 u32 prod = buf->prod, cons = buf->cons;
keir@21759 451 s32 x;
keir@21752 452
keir@21759 453 barrier(); /* must read buf->prod and buf->cons only once */
keir@21752 454 if ( bogus(prod, cons) )
keir@21752 455 return data_size;
keir@21752 456
keir@21759 457 x = prod - cons;
keir@17038 458 if ( x < 0 )
keir@17038 459 x += 2*data_size;
keir@17038 460
keir@17038 461 ASSERT(x >= 0);
keir@17038 462 ASSERT(x <= data_size);
keir@17038 463
keir@17038 464 return x;
keir@17038 465 }
keir@17038 466
keir@21752 467 static inline u32 calc_bytes_to_wrap(const struct t_buf *buf)
gdunlap@15968 468 {
keir@21759 469 u32 prod = buf->prod, cons = buf->cons;
keir@21759 470 s32 x;
keir@21752 471
keir@21759 472 barrier(); /* must read buf->prod and buf->cons only once */
keir@21759 473 if ( bogus(prod, cons) )
keir@21752 474 return 0;
keir@21752 475
keir@21759 476 x = data_size - prod;
keir@17038 477 if ( x <= 0 )
keir@17038 478 x += data_size;
keir@17038 479
keir@17038 480 ASSERT(x > 0);
keir@17038 481 ASSERT(x <= data_size);
keir@17038 482
keir@17038 483 return x;
gdunlap@15968 484 }
gdunlap@15968 485
keir@21752 486 static inline u32 calc_bytes_avail(const struct t_buf *buf)
keir@17038 487 {
keir@17038 488 return data_size - calc_unconsumed_bytes(buf);
keir@17038 489 }
keir@17038 490
keir@21759 491 static inline struct t_rec *next_record(const struct t_buf *buf,
keir@21759 492 uint32_t *next)
gdunlap@15968 493 {
keir@21759 494 u32 x = buf->prod, cons = buf->cons;
keir@21752 495
keir@21759 496 barrier(); /* must read buf->prod and buf->cons only once */
keir@21759 497 *next = x;
keir@21759 498 if ( !tb_init_done || bogus(x, cons) )
keir@21752 499 return NULL;
keir@21752 500
keir@17038 501 if ( x >= data_size )
keir@17038 502 x -= data_size;
keir@17038 503
keir@17038 504 ASSERT(x < data_size);
keir@17038 505
keir@17038 506 return (struct t_rec *)&this_cpu(t_data)[x];
gdunlap@15968 507 }
gdunlap@15968 508
keir@21752 509 static inline void __insert_record(struct t_buf *buf,
keir@21752 510 unsigned long event,
keir@21752 511 unsigned int extra,
keir@21752 512 bool_t cycles,
keir@21752 513 unsigned int rec_size,
keir@21752 514 const void *extra_data)
gdunlap@15968 515 {
gdunlap@15968 516 struct t_rec *rec;
keir@15979 517 unsigned char *dst;
keir@21752 518 unsigned int extra_word = extra / sizeof(u32);
keir@21752 519 unsigned int local_rec_size = calc_rec_size(cycles, extra);
keir@17038 520 uint32_t next;
gdunlap@15968 521
gdunlap@15968 522 BUG_ON(local_rec_size != rec_size);
keir@17038 523 BUG_ON(extra & 3);
gdunlap@15968 524
keir@21759 525 rec = next_record(buf, &next);
keir@21759 526 if ( !rec )
keir@21759 527 return;
gdunlap@15968 528 /* Double-check once more that we have enough space.
gdunlap@15968 529 * Don't bugcheck here, in case the userland tool is doing
gdunlap@15968 530 * something stupid. */
keir@21759 531 if ( (unsigned char *)rec + rec_size > this_cpu(t_data) + data_size )
gdunlap@15968 532 {
keir@21751 533 if ( printk_ratelimit() )
keir@21751 534 printk(XENLOG_WARNING
keir@21759 535 "%s: size=%08x prod=%08x cons=%08x rec=%u\n",
keir@21759 536 __func__, data_size, next, buf->cons, rec_size);
keir@21752 537 return;
gdunlap@15968 538 }
gdunlap@15968 539
keir@15979 540 rec->event = event;
keir@15979 541 rec->extra_u32 = extra_word;
keir@15979 542 dst = (unsigned char *)rec->u.nocycles.extra_u32;
keir@15979 543 if ( (rec->cycles_included = cycles) != 0 )
gdunlap@15968 544 {
gdunlap@15968 545 u64 tsc = (u64)get_cycles();
keir@15979 546 rec->u.cycles.cycles_lo = (uint32_t)tsc;
keir@15979 547 rec->u.cycles.cycles_hi = (uint32_t)(tsc >> 32);
keir@15979 548 dst = (unsigned char *)rec->u.cycles.extra_u32;
keir@15979 549 }
gdunlap@15968 550
keir@15979 551 if ( extra_data && extra )
gdunlap@15968 552 memcpy(dst, extra_data, extra);
gdunlap@15968 553
gdunlap@15968 554 wmb();
keir@17038 555
keir@21752 556 next += rec_size;
keir@17038 557 if ( next >= 2*data_size )
keir@17038 558 next -= 2*data_size;
keir@17038 559 ASSERT(next < 2*data_size);
keir@17038 560 buf->prod = next;
gdunlap@15968 561 }
gdunlap@15968 562
keir@21752 563 static inline void insert_wrap_record(struct t_buf *buf,
keir@21752 564 unsigned int size)
gdunlap@15968 565 {
keir@21752 566 u32 space_left = calc_bytes_to_wrap(buf);
keir@21752 567 unsigned int extra_space = space_left - sizeof(u32);
keir@21752 568 bool_t cycles = 0;
gdunlap@15968 569
gdunlap@15968 570 BUG_ON(space_left > size);
gdunlap@15968 571
gdunlap@15968 572 /* We may need to add cycles to take up enough space... */
keir@15979 573 if ( (extra_space/sizeof(u32)) > TRACE_EXTRA_MAX )
gdunlap@15968 574 {
gdunlap@15968 575 cycles = 1;
gdunlap@15968 576 extra_space -= sizeof(u64);
gdunlap@15968 577 ASSERT((extra_space/sizeof(u32)) <= TRACE_EXTRA_MAX);
gdunlap@15968 578 }
gdunlap@15968 579
keir@21752 580 __insert_record(buf, TRC_TRACE_WRAP_BUFFER, extra_space, cycles,
keir@21752 581 space_left, NULL);
gdunlap@15968 582 }
gdunlap@15968 583
keir@18476 584 #define LOST_REC_SIZE (4 + 8 + 16) /* header + tsc + sizeof(struct ed) */
gdunlap@15968 585
keir@21752 586 static inline void insert_lost_records(struct t_buf *buf)
gdunlap@15968 587 {
gdunlap@15968 588 struct {
gdunlap@15968 589 u32 lost_records;
keir@18476 590 u32 did:16, vid:16;
keir@18476 591 u64 first_tsc;
keir@18476 592 } __attribute__((packed)) ed;
gdunlap@15968 593
keir@18476 594 ed.vid = current->vcpu_id;
keir@18476 595 ed.did = current->domain->domain_id;
gdunlap@15968 596 ed.lost_records = this_cpu(lost_records);
keir@18476 597 ed.first_tsc = this_cpu(lost_records_first_tsc);
gdunlap@15968 598
gdunlap@15968 599 this_cpu(lost_records) = 0;
gdunlap@15968 600
keir@21752 601 __insert_record(buf, TRC_LOST_RECORDS, sizeof(ed), 1 /* cycles */,
keir@21752 602 LOST_REC_SIZE, &ed);
gdunlap@15968 603 }
gdunlap@15968 604
keir@17552 605 /*
keir@17552 606 * Notification is performed in qtasklet to avoid deadlocks with contexts
keir@17552 607 * which __trace_var() may be called from (e.g., scheduler critical regions).
keir@17552 608 */
keir@17552 609 static void trace_notify_dom0(unsigned long unused)
keir@17552 610 {
keir@17552 611 send_guest_global_virq(dom0, VIRQ_TBUF);
keir@17552 612 }
keir@17552 613 static DECLARE_TASKLET(trace_notify_dom0_tasklet, trace_notify_dom0, 0);
gdunlap@15968 614
kaf24@7606 615 /**
kaf24@7606 616 * trace - Enters a trace tuple into the trace buffer for the current CPU.
kaf24@7606 617 * @event: the event type being logged
kaf24@7606 618 * @d1...d5: the data items for the event being logged
kaf24@7606 619 *
kaf24@7606 620 * Logs a trace record into the appropriate buffer. Returns nonzero on
kaf24@7606 621 * failure, otherwise 0. Failure occurs only if the trace buffers are not yet
kaf24@7606 622 * initialised.
kaf24@7606 623 */
keir@21752 624 void __trace_var(u32 event, bool_t cycles, unsigned int extra,
keir@21752 625 const void *extra_data)
kaf24@7606 626 {
kaf24@7606 627 struct t_buf *buf;
keir@21752 628 unsigned long flags;
keir@21752 629 u32 bytes_to_tail, bytes_to_wrap;
keir@21752 630 unsigned int rec_size, total_size;
keir@21752 631 unsigned int extra_word;
keir@21752 632 bool_t started_below_highwater;
gdunlap@15968 633
keir@18479 634 if( !tb_init_done )
keir@18479 635 return;
gdunlap@15968 636
gdunlap@15968 637 /* Convert byte count into word count, rounding up */
gdunlap@15968 638 extra_word = (extra / sizeof(u32));
keir@16111 639 if ( (extra % sizeof(u32)) != 0 )
gdunlap@15968 640 extra_word++;
kfraser@10698 641
keir@16111 642 ASSERT(extra_word <= TRACE_EXTRA_MAX);
keir@16111 643 extra_word = min_t(int, extra_word, TRACE_EXTRA_MAX);
gdunlap@15968 644
gdunlap@15968 645 /* Round size up to nearest word */
gdunlap@15968 646 extra = extra_word * sizeof(u32);
kaf24@7606 647
kaf24@7606 648 if ( (tb_event_mask & event) == 0 )
kaf24@7606 649 return;
kaf24@7606 650
kaf24@7606 651 /* match class */
kaf24@7606 652 if ( ((tb_event_mask >> TRC_CLS_SHIFT) & (event >> TRC_CLS_SHIFT)) == 0 )
kaf24@7606 653 return;
kaf24@7606 654
kaf24@7606 655 /* then match subclass */
kaf24@7606 656 if ( (((tb_event_mask >> TRC_SUBCLS_SHIFT) & 0xf )
kaf24@7606 657 & ((event >> TRC_SUBCLS_SHIFT) & 0xf )) == 0 )
kaf24@7606 658 return;
kaf24@7606 659
kfraser@11295 660 if ( !cpu_isset(smp_processor_id(), tb_cpu_mask) )
kaf24@7606 661 return;
kaf24@7606 662
kaf24@7606 663 /* Read tb_init_done /before/ t_bufs. */
kaf24@7606 664 rmb();
kaf24@7606 665
keir@20873 666 spin_lock_irqsave(&this_cpu(t_lock), flags);
keir@20873 667
kaf24@11022 668 buf = this_cpu(t_bufs);
kaf24@7606 669
keir@20873 670 if ( unlikely(!buf) )
keir@21752 671 {
keir@21752 672 /* Make gcc happy */
keir@21752 673 started_below_highwater = 0;
keir@20873 674 goto unlock;
keir@21752 675 }
kaf24@7609 676
keir@17038 677 started_below_highwater = (calc_unconsumed_bytes(buf) < t_buf_highwater);
gdunlap@15968 678
gdunlap@15968 679 /* Calculate the record size */
gdunlap@15968 680 rec_size = calc_rec_size(cycles, extra);
gdunlap@15968 681
gdunlap@15968 682 /* How many bytes are available in the buffer? */
gdunlap@15968 683 bytes_to_tail = calc_bytes_avail(buf);
gdunlap@15968 684
gdunlap@15968 685 /* How many bytes until the next wrap-around? */
gdunlap@15968 686 bytes_to_wrap = calc_bytes_to_wrap(buf);
gdunlap@15968 687
gdunlap@15968 688 /*
gdunlap@15968 689 * Calculate expected total size to commit this record by
gdunlap@15968 690 * doing a dry-run.
gdunlap@15968 691 */
gdunlap@15968 692 total_size = 0;
gdunlap@15968 693
gdunlap@15968 694 /* First, check to see if we need to include a lost_record.
gdunlap@15968 695 */
keir@15979 696 if ( this_cpu(lost_records) )
gdunlap@15968 697 {
keir@15979 698 if ( LOST_REC_SIZE > bytes_to_wrap )
gdunlap@15968 699 {
gdunlap@15968 700 total_size += bytes_to_wrap;
gdunlap@15968 701 bytes_to_wrap = data_size;
gdunlap@15968 702 }
gdunlap@15968 703 total_size += LOST_REC_SIZE;
keir@17037 704 bytes_to_wrap -= LOST_REC_SIZE;
keir@17037 705
keir@17037 706 /* LOST_REC might line up perfectly with the buffer wrap */
keir@17037 707 if ( bytes_to_wrap == 0 )
keir@17037 708 bytes_to_wrap = data_size;
gdunlap@15968 709 }
gdunlap@15968 710
keir@15979 711 if ( rec_size > bytes_to_wrap )
gdunlap@15968 712 {
gdunlap@15968 713 total_size += bytes_to_wrap;
gdunlap@15968 714 }
gdunlap@15968 715 total_size += rec_size;
gdunlap@15968 716
gdunlap@15968 717 /* Do we have enough space for everything? */
keir@15979 718 if ( total_size > bytes_to_tail )
kaf24@7606 719 {
keir@18476 720 if ( ++this_cpu(lost_records) == 1 )
keir@18476 721 this_cpu(lost_records_first_tsc)=(u64)get_cycles();
keir@20873 722 started_below_highwater = 0;
keir@20873 723 goto unlock;
kaf24@7606 724 }
kaf24@7606 725
gdunlap@15968 726 /*
gdunlap@15968 727 * Now, actually write information
gdunlap@15968 728 */
gdunlap@15968 729 bytes_to_wrap = calc_bytes_to_wrap(buf);
gdunlap@15968 730
keir@15979 731 if ( this_cpu(lost_records) )
kfraser@10698 732 {
keir@15979 733 if ( LOST_REC_SIZE > bytes_to_wrap )
ack@13311 734 {
gdunlap@15968 735 insert_wrap_record(buf, LOST_REC_SIZE);
gdunlap@15968 736 bytes_to_wrap = data_size;
gdunlap@15968 737 }
gdunlap@15968 738 insert_lost_records(buf);
keir@17037 739 bytes_to_wrap -= LOST_REC_SIZE;
keir@17037 740
keir@17037 741 /* LOST_REC might line up perfectly with the buffer wrap */
keir@17037 742 if ( bytes_to_wrap == 0 )
keir@17037 743 bytes_to_wrap = data_size;
kfraser@10698 744 }
kfraser@10698 745
keir@15979 746 if ( rec_size > bytes_to_wrap )
gdunlap@15968 747 insert_wrap_record(buf, rec_size);
kaf24@7609 748
gdunlap@15968 749 /* Write the original record */
gdunlap@15968 750 __insert_record(buf, event, extra, cycles, rec_size, extra_data);
kaf24@7609 751
keir@20873 752 unlock:
keir@20873 753 spin_unlock_irqrestore(&this_cpu(t_lock), flags);
kaf24@9625 754
keir@15979 755 /* Notify trace buffer consumer that we've crossed the high water mark. */
keir@21752 756 if ( likely(buf!=NULL)
keir@21752 757 && started_below_highwater
keir@21752 758 && (calc_unconsumed_bytes(buf) >= t_buf_highwater) )
keir@17552 759 tasklet_schedule(&trace_notify_dom0_tasklet);
kaf24@7606 760 }
kaf24@7606 761
kaf24@3952 762 /*
kaf24@3952 763 * Local variables:
kaf24@3952 764 * mode: C
kaf24@3952 765 * c-set-style: "BSD"
kaf24@3952 766 * c-basic-offset: 4
kaf24@3952 767 * tab-width: 4
kaf24@3952 768 * indent-tabs-mode: nil
kaf24@4026 769 * End:
kaf24@3952 770 */