1/************************************************************************** 2 * 3 * Copyright 2008 VMware, Inc. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28 29/** 30 * @file 31 * Trace dumping functions. 32 * 33 * For now we just use standard XML for dumping the trace calls, as this is 34 * simple to write, parse, and visually inspect, but the actual representation 35 * is abstracted out of this file, so that we can switch to a binary 36 * representation if/when it becomes justified. 37 * 38 * @author Jose Fonseca <jfonseca@vmware.com> 39 */ 40 41#include "pipe/p_config.h" 42 43#include <stdio.h> 44#include <stdlib.h> 45 46/* for access() */ 47#ifdef _WIN32 48# include <io.h> 49#endif 50 51#include "pipe/p_compiler.h" 52#include "os/os_thread.h" 53#include "util/os_time.h" 54#include "util/u_debug.h" 55#include "util/u_memory.h" 56#include "util/u_string.h" 57#include "util/u_math.h" 58#include "util/format/u_format.h" 59#include "compiler/nir/nir.h" 60 61#include "tr_dump.h" 62#include "tr_screen.h" 63#include "tr_texture.h" 64 65 66static bool close_stream = false; 67static FILE *stream = NULL; 68static mtx_t call_mutex = _MTX_INITIALIZER_NP; 69static long unsigned call_no = 0; 70static bool dumping = false; 71static long nir_count = 0; 72 73static bool trigger_active = true; 74static char *trigger_filename = NULL; 75 76void 77trace_dump_trigger_active(bool active) 78{ 79 trigger_active = active; 80} 81 82void 83trace_dump_check_trigger(void) 84{ 85 if (!trigger_filename) 86 return; 87 88 mtx_lock(&call_mutex); 89 if (trigger_active) { 90 trigger_active = false; 91 } else { 92 if (!access(trigger_filename, 2 /* W_OK but compiles on Windows */)) { 93 if (!unlink(trigger_filename)) { 94 trigger_active = true; 95 } else { 96 fprintf(stderr, "error removing trigger file\n"); 97 trigger_active = false; 98 } 99 } 100 } 101 mtx_unlock(&call_mutex); 102} 103 104bool 105trace_dump_is_triggered(void) 106{ 107 return trigger_active && !!trigger_filename; 108} 109 110static inline void 111trace_dump_write(const char *buf, size_t size) 112{ 113 if (stream && trigger_active) { 114 fwrite(buf, size, 1, stream); 115 } 116} 117 118 119static inline void 120trace_dump_writes(const char *s) 121{ 122 trace_dump_write(s, strlen(s)); 123} 124 125 126static inline void 127trace_dump_writef(const char *format, ...) 128{ 129 static char buf[1024]; 130 unsigned len; 131 va_list ap; 132 va_start(ap, format); 133 len = vsnprintf(buf, sizeof(buf), format, ap); 134 va_end(ap); 135 trace_dump_write(buf, len); 136} 137 138 139static inline void 140trace_dump_escape(const char *str) 141{ 142 const unsigned char *p = (const unsigned char *)str; 143 unsigned char c; 144 while((c = *p++) != 0) { 145 if(c == '<') 146 trace_dump_writes("<"); 147 else if(c == '>') 148 trace_dump_writes(">"); 149 else if(c == '&') 150 trace_dump_writes("&"); 151 else if(c == '\'') 152 trace_dump_writes("'"); 153 else if(c == '\"') 154 trace_dump_writes("""); 155 else if(c >= 0x20 && c <= 0x7e) 156 trace_dump_writef("%c", c); 157 else 158 trace_dump_writef("&#%u;", c); 159 } 160} 161 162 163static inline void 164trace_dump_indent(unsigned level) 165{ 166 unsigned i; 167 for(i = 0; i < level; ++i) 168 trace_dump_writes("\t"); 169} 170 171 172static inline void 173trace_dump_newline(void) 174{ 175 trace_dump_writes("\n"); 176} 177 178 179static inline void 180trace_dump_tag_begin(const char *name) 181{ 182 trace_dump_writes("<"); 183 trace_dump_writes(name); 184 trace_dump_writes(">"); 185} 186 187static inline void 188trace_dump_tag_begin1(const char *name, 189 const char *attr1, const char *value1) 190{ 191 trace_dump_writes("<"); 192 trace_dump_writes(name); 193 trace_dump_writes(" "); 194 trace_dump_writes(attr1); 195 trace_dump_writes("='"); 196 trace_dump_escape(value1); 197 trace_dump_writes("'>"); 198} 199 200 201static inline void 202trace_dump_tag_end(const char *name) 203{ 204 trace_dump_writes("</"); 205 trace_dump_writes(name); 206 trace_dump_writes(">"); 207} 208 209void 210trace_dump_trace_flush(void) 211{ 212 if (stream) { 213 fflush(stream); 214 } 215} 216 217static void 218trace_dump_trace_close(void) 219{ 220 if (stream) { 221 trigger_active = true; 222 trace_dump_writes("</trace>\n"); 223 if (close_stream) { 224 fclose(stream); 225 close_stream = false; 226 stream = NULL; 227 } 228 call_no = 0; 229 free(trigger_filename); 230 } 231} 232 233 234static void 235trace_dump_call_time(int64_t time) 236{ 237 if (stream) { 238 trace_dump_indent(2); 239 trace_dump_tag_begin("time"); 240 trace_dump_int(time); 241 trace_dump_tag_end("time"); 242 trace_dump_newline(); 243 } 244} 245 246 247bool 248trace_dump_trace_begin(void) 249{ 250 const char *filename; 251 252 filename = debug_get_option("GALLIUM_TRACE", NULL); 253 if (!filename) 254 return false; 255 256 nir_count = debug_get_num_option("GALLIUM_TRACE_NIR", 32); 257 258 if (!stream) { 259 260 if (strcmp(filename, "stderr") == 0) { 261 close_stream = false; 262 stream = stderr; 263 } 264 else if (strcmp(filename, "stdout") == 0) { 265 close_stream = false; 266 stream = stdout; 267 } 268 else { 269 close_stream = true; 270 stream = fopen(filename, "wt"); 271 if (!stream) 272 return false; 273 } 274 275 trace_dump_writes("<?xml version='1.0' encoding='UTF-8'?>\n"); 276 trace_dump_writes("<?xml-stylesheet type='text/xsl' href='trace.xsl'?>\n"); 277 trace_dump_writes("<trace version='0.1'>\n"); 278 279 /* Many applications don't exit cleanly, others may create and destroy a 280 * screen multiple times, so we only write </trace> tag and close at exit 281 * time. 282 */ 283 atexit(trace_dump_trace_close); 284 285 const char *trigger = debug_get_option("GALLIUM_TRACE_TRIGGER", NULL); 286 if (trigger) { 287 trigger_filename = strdup(trigger); 288 trigger_active = false; 289 } else 290 trigger_active = true; 291 } 292 293 return true; 294} 295 296bool trace_dump_trace_enabled(void) 297{ 298 return stream ? true : false; 299} 300 301/* 302 * Call lock 303 */ 304 305void trace_dump_call_lock(void) 306{ 307 mtx_lock(&call_mutex); 308} 309 310void trace_dump_call_unlock(void) 311{ 312 mtx_unlock(&call_mutex); 313} 314 315/* 316 * Dumping control 317 */ 318 319void trace_dumping_start_locked(void) 320{ 321 dumping = true; 322} 323 324void trace_dumping_stop_locked(void) 325{ 326 dumping = false; 327} 328 329bool trace_dumping_enabled_locked(void) 330{ 331 return dumping; 332} 333 334void trace_dumping_start(void) 335{ 336 mtx_lock(&call_mutex); 337 trace_dumping_start_locked(); 338 mtx_unlock(&call_mutex); 339} 340 341void trace_dumping_stop(void) 342{ 343 mtx_lock(&call_mutex); 344 trace_dumping_stop_locked(); 345 mtx_unlock(&call_mutex); 346} 347 348bool trace_dumping_enabled(void) 349{ 350 bool ret; 351 mtx_lock(&call_mutex); 352 ret = trace_dumping_enabled_locked(); 353 mtx_unlock(&call_mutex); 354 return ret; 355} 356 357/* 358 * Dump functions 359 */ 360 361static int64_t call_start_time = 0; 362 363void trace_dump_call_begin_locked(const char *klass, const char *method) 364{ 365 if (!dumping) 366 return; 367 368 ++call_no; 369 trace_dump_indent(1); 370 trace_dump_writes("<call no=\'"); 371 trace_dump_writef("%lu", call_no); 372 trace_dump_writes("\' class=\'"); 373 trace_dump_escape(klass); 374 trace_dump_writes("\' method=\'"); 375 trace_dump_escape(method); 376 trace_dump_writes("\'>"); 377 trace_dump_newline(); 378 379 call_start_time = os_time_get(); 380} 381 382void trace_dump_call_end_locked(void) 383{ 384 int64_t call_end_time; 385 386 if (!dumping) 387 return; 388 389 call_end_time = os_time_get(); 390 391 trace_dump_call_time(call_end_time - call_start_time); 392 trace_dump_indent(1); 393 trace_dump_tag_end("call"); 394 trace_dump_newline(); 395 fflush(stream); 396} 397 398void trace_dump_call_begin(const char *klass, const char *method) 399{ 400 mtx_lock(&call_mutex); 401 trace_dump_call_begin_locked(klass, method); 402} 403 404void trace_dump_call_end(void) 405{ 406 trace_dump_call_end_locked(); 407 mtx_unlock(&call_mutex); 408} 409 410void trace_dump_arg_begin(const char *name) 411{ 412 if (!dumping) 413 return; 414 415 trace_dump_indent(2); 416 trace_dump_tag_begin1("arg", "name", name); 417} 418 419void trace_dump_arg_end(void) 420{ 421 if (!dumping) 422 return; 423 424 trace_dump_tag_end("arg"); 425 trace_dump_newline(); 426} 427 428void trace_dump_ret_begin(void) 429{ 430 if (!dumping) 431 return; 432 433 trace_dump_indent(2); 434 trace_dump_tag_begin("ret"); 435} 436 437void trace_dump_ret_end(void) 438{ 439 if (!dumping) 440 return; 441 442 trace_dump_tag_end("ret"); 443 trace_dump_newline(); 444} 445 446void trace_dump_bool(int value) 447{ 448 if (!dumping) 449 return; 450 451 trace_dump_writef("<bool>%c</bool>", value ? '1' : '0'); 452} 453 454void trace_dump_int(long long int value) 455{ 456 if (!dumping) 457 return; 458 459 trace_dump_writef("<int>%lli</int>", value); 460} 461 462void trace_dump_uint(long long unsigned value) 463{ 464 if (!dumping) 465 return; 466 467 trace_dump_writef("<uint>%llu</uint>", value); 468} 469 470void trace_dump_float(double value) 471{ 472 if (!dumping) 473 return; 474 475 trace_dump_writef("<float>%g</float>", value); 476} 477 478void trace_dump_bytes(const void *data, 479 size_t size) 480{ 481 static const char hex_table[16] = "0123456789ABCDEF"; 482 const uint8_t *p = data; 483 size_t i; 484 485 if (!dumping) 486 return; 487 488 trace_dump_writes("<bytes>"); 489 for(i = 0; i < size; ++i) { 490 uint8_t byte = *p++; 491 char hex[2]; 492 hex[0] = hex_table[byte >> 4]; 493 hex[1] = hex_table[byte & 0xf]; 494 trace_dump_write(hex, 2); 495 } 496 trace_dump_writes("</bytes>"); 497} 498 499void trace_dump_box_bytes(const void *data, 500 struct pipe_resource *resource, 501 const struct pipe_box *box, 502 unsigned stride, 503 unsigned slice_stride) 504{ 505 enum pipe_format format = resource->format; 506 size_t size; 507 508 assert(box->height > 0); 509 assert(box->depth > 0); 510 511 size = util_format_get_nblocksx(format, box->width ) * util_format_get_blocksize(format) 512 + (util_format_get_nblocksy(format, box->height) - 1) * stride 513 + (box->depth - 1) * slice_stride; 514 515 /* 516 * Only dump buffer transfers to avoid huge files. 517 * TODO: Make this run-time configurable 518 */ 519 if (resource->target != PIPE_BUFFER) { 520 size = 0; 521 } 522 523 trace_dump_bytes(data, size); 524} 525 526void trace_dump_string(const char *str) 527{ 528 if (!dumping) 529 return; 530 531 trace_dump_writes("<string>"); 532 trace_dump_escape(str); 533 trace_dump_writes("</string>"); 534} 535 536void trace_dump_enum(const char *value) 537{ 538 if (!dumping) 539 return; 540 541 trace_dump_writes("<enum>"); 542 trace_dump_escape(value); 543 trace_dump_writes("</enum>"); 544} 545 546void trace_dump_array_begin(void) 547{ 548 if (!dumping) 549 return; 550 551 trace_dump_writes("<array>"); 552} 553 554void trace_dump_array_end(void) 555{ 556 if (!dumping) 557 return; 558 559 trace_dump_writes("</array>"); 560} 561 562void trace_dump_elem_begin(void) 563{ 564 if (!dumping) 565 return; 566 567 trace_dump_writes("<elem>"); 568} 569 570void trace_dump_elem_end(void) 571{ 572 if (!dumping) 573 return; 574 575 trace_dump_writes("</elem>"); 576} 577 578void trace_dump_struct_begin(const char *name) 579{ 580 if (!dumping) 581 return; 582 583 trace_dump_writef("<struct name='%s'>", name); 584} 585 586void trace_dump_struct_end(void) 587{ 588 if (!dumping) 589 return; 590 591 trace_dump_writes("</struct>"); 592} 593 594void trace_dump_member_begin(const char *name) 595{ 596 if (!dumping) 597 return; 598 599 trace_dump_writef("<member name='%s'>", name); 600} 601 602void trace_dump_member_end(void) 603{ 604 if (!dumping) 605 return; 606 607 trace_dump_writes("</member>"); 608} 609 610void trace_dump_null(void) 611{ 612 if (!dumping) 613 return; 614 615 trace_dump_writes("<null/>"); 616} 617 618void trace_dump_ptr(const void *value) 619{ 620 if (!dumping) 621 return; 622 623 if(value) 624 trace_dump_writef("<ptr>0x%08lx</ptr>", (unsigned long)(uintptr_t)value); 625 else 626 trace_dump_null(); 627} 628 629void trace_dump_surface_ptr(struct pipe_surface *_surface) 630{ 631 if (!dumping) 632 return; 633 634 if (_surface) { 635 struct trace_surface *tr_surf = trace_surface(_surface); 636 trace_dump_ptr(tr_surf->surface); 637 } else { 638 trace_dump_null(); 639 } 640} 641 642void trace_dump_transfer_ptr(struct pipe_transfer *_transfer) 643{ 644 if (!dumping) 645 return; 646 647 if (_transfer) { 648 struct trace_transfer *tr_tran = trace_transfer(_transfer); 649 trace_dump_ptr(tr_tran->transfer); 650 } else { 651 trace_dump_null(); 652 } 653} 654 655void trace_dump_nir(void *nir) 656{ 657 if (!dumping) 658 return; 659 660 if (nir_count < 0) { 661 fputs("<string>...</string>", stream); 662 return; 663 } 664 665 if ((nir_count--) == 0) { 666 fputs("<string>Set GALLIUM_TRACE_NIR to a sufficiently big number " 667 "to enable NIR shader dumping.</string>", stream); 668 return; 669 } 670 671 // NIR doesn't have a print to string function. Use CDATA and hope for the 672 // best. 673 if (stream) { 674 fputs("<string><![CDATA[", stream); 675 nir_print_shader(nir, stream); 676 fputs("]]></string>", stream); 677 } 678} 679