1# 2# Copyright (C) 2020 Google, Inc. 3# 4# Permission is hereby granted, free of charge, to any person obtaining a 5# copy of this software and associated documentation files (the "Software"), 6# to deal in the Software without restriction, including without limitation 7# the rights to use, copy, modify, merge, publish, distribute, sublicense, 8# and/or sell copies of the Software, and to permit persons to whom the 9# Software is furnished to do so, subject to the following conditions: 10# 11# The above copyright notice and this permission notice (including the next 12# paragraph) shall be included in all copies or substantial portions of the 13# Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21# IN THE SOFTWARE. 22# 23 24from mako.template import Template 25from collections import namedtuple 26from enum import IntEnum 27import os 28 29TRACEPOINTS = {} 30TRACEPOINTS_TOGGLES = {} 31 32class Tracepoint(object): 33 """Class that represents all the information about a tracepoint 34 """ 35 def __init__(self, name, args=[], toggle_name=None, 36 tp_struct=None, tp_print=None, tp_perfetto=None, 37 tp_markers=None, end_of_pipe=False, need_cs_param=True): 38 """Parameters: 39 40 - name: the tracepoint name, a tracepoint function with the given 41 name (prefixed by 'trace_') will be generated with the specied 42 args (following a u_trace ptr). Calling this tracepoint will 43 emit a trace, if tracing is enabled. 44 - args: the tracepoint func args, an array of TracepointArg 45 - tp_print: (optional) array of format string followed by expressions 46 - tp_perfetto: (optional) driver provided callback which can generate 47 perfetto events 48 - tp_markers: (optional) driver provided printf-style callback which can 49 generate CS markers, this requires 'need_cs_param' as the first param 50 is the CS that the label should be emitted into 51 - need_cs_param: whether tracepoint functions need an additional cs 52 parameter. 53 """ 54 assert isinstance(name, str) 55 assert isinstance(args, list) 56 assert name not in TRACEPOINTS 57 58 59 self.name = name 60 self.args = args 61 if tp_struct is None: 62 tp_struct = args 63 self.tp_struct = tp_struct 64 self.has_variable_arg = False 65 for arg in self.tp_struct: 66 if arg.length_arg != None: 67 self.has_variable_arg = True 68 break 69 self.tp_print = tp_print 70 self.tp_perfetto = tp_perfetto 71 self.tp_markers = tp_markers 72 self.end_of_pipe = end_of_pipe 73 self.toggle_name = toggle_name 74 self.need_cs_param = need_cs_param 75 76 TRACEPOINTS[name] = self 77 if toggle_name is not None and toggle_name not in TRACEPOINTS_TOGGLES: 78 TRACEPOINTS_TOGGLES[toggle_name] = len(TRACEPOINTS_TOGGLES) 79 80 def can_generate_print(self): 81 return self.args is not None and len(self.args) > 0 82 83 def enabled_expr(self, trace_toggle_name): 84 if trace_toggle_name is None: 85 return "true" 86 assert self.toggle_name is not None 87 return "({0} & {1}_{2})".format(trace_toggle_name, 88 trace_toggle_name.upper(), 89 self.toggle_name.upper()) 90 91class TracepointArgStruct(): 92 """Represents struct that is being passed as an argument 93 """ 94 def __init__(self, type, var): 95 """Parameters: 96 97 - type: argument's C type. 98 - var: name of the argument 99 """ 100 assert isinstance(type, str) 101 assert isinstance(var, str) 102 103 self.type = type 104 self.var = var 105 106class TracepointArg(object): 107 """Class that represents either an argument being passed or a field in a struct 108 """ 109 def __init__(self, type, var, c_format, name=None, to_prim_type=None, length_arg=None, copy_func=None): 110 """Parameters: 111 112 - type: argument's C type. 113 - var: either an argument name or a field in the struct 114 - c_format: printf format to print the value. 115 - name: (optional) name that will be used in intermidiate structs and will 116 be displayed in output or perfetto, otherwise var will be used. 117 - to_prim_type: (optional) C function to convert from arg's type to a type 118 compatible with c_format. 119 - length_arg: whether this argument is a variable length array 120 """ 121 assert isinstance(type, str) 122 assert isinstance(var, str) 123 assert isinstance(c_format, str) 124 125 self.type = type 126 self.var = var 127 self.c_format = c_format 128 if name is None: 129 name = var 130 self.name = name 131 self.to_prim_type = to_prim_type 132 self.length_arg = length_arg 133 self.copy_func = copy_func 134 135 136HEADERS = [] 137 138class HeaderScope(IntEnum): 139 HEADER = (1 << 0) 140 SOURCE = (1 << 1) 141 PERFETTO = (1 << 2) 142 143class Header(object): 144 """Class that represents a header file dependency of generated tracepoints 145 """ 146 def __init__(self, hdr, scope=HeaderScope.HEADER): 147 """Parameters: 148 149 - hdr: the required header path 150 """ 151 assert isinstance(hdr, str) 152 self.hdr = hdr 153 self.scope = scope 154 155 HEADERS.append(self) 156 157 158FORWARD_DECLS = [] 159 160class ForwardDecl(object): 161 """Class that represents a forward declaration 162 """ 163 def __init__(self, decl): 164 assert isinstance(decl, str) 165 self.decl = decl 166 167 FORWARD_DECLS.append(self) 168 169 170hdr_template = """\ 171/* Copyright (C) 2020 Google, Inc. 172 * 173 * Permission is hereby granted, free of charge, to any person obtaining a 174 * copy of this software and associated documentation files (the "Software"), 175 * to deal in the Software without restriction, including without limitation 176 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 177 * and/or sell copies of the Software, and to permit persons to whom the 178 * Software is furnished to do so, subject to the following conditions: 179 * 180 * The above copyright notice and this permission notice (including the next 181 * paragraph) shall be included in all copies or substantial portions of the 182 * Software. 183 * 184 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 185 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 186 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 187 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 188 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 189 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 190 * IN THE SOFTWARE. 191 */ 192 193<% guard_name = '_' + hdrname + '_H' %> 194#ifndef ${guard_name} 195#define ${guard_name} 196 197% for header in HEADERS: 198#include "${header.hdr}" 199% endfor 200 201#include "util/perf/u_trace.h" 202 203#ifdef __cplusplus 204extern "C" { 205#endif 206 207% for declaration in FORWARD_DECLS: 208${declaration.decl}; 209% endfor 210 211% if trace_toggle_name is not None: 212enum ${trace_toggle_name.lower()} { 213% for toggle_name, config_id in TRACEPOINTS_TOGGLES.items(): 214 ${trace_toggle_name.upper()}_${toggle_name.upper()} = 1ull << ${config_id}, 215% endfor 216}; 217 218extern uint64_t ${trace_toggle_name}; 219 220void ${trace_toggle_name}_config_variable(void); 221% endif 222 223% for trace_name, trace in TRACEPOINTS.items(): 224 225/* 226 * ${trace_name} 227 */ 228struct trace_${trace_name} { 229% for arg in trace.tp_struct: 230 ${arg.type} ${arg.name}${"[0]" if arg.length_arg else ""}; 231% endfor 232% if len(trace.args) == 0: 233#ifdef __cplusplus 234 /* avoid warnings about empty struct size mis-match in C vs C++.. 235 * the size mis-match is harmless because (a) nothing will deref 236 * the empty struct, and (b) the code that cares about allocating 237 * sizeof(struct trace_${trace_name}) (and wants this to be zero 238 * if there is no payload) is C 239 */ 240 uint8_t dummy; 241#endif 242% endif 243}; 244% if trace.tp_perfetto is not None: 245#ifdef HAVE_PERFETTO 246void ${trace.tp_perfetto}( 247 ${ctx_param}, 248 uint64_t ts_ns, 249 uint16_t tp_idx, 250 const void *flush_data, 251 const struct trace_${trace_name} *payload); 252#endif 253% endif 254void __trace_${trace_name}( 255 struct u_trace *ut 256 , enum u_trace_type enabled_traces 257% if trace.need_cs_param: 258 , void *cs 259% endif 260% for arg in trace.args: 261 , ${arg.type} ${arg.var} 262% endfor 263); 264static ALWAYS_INLINE void trace_${trace_name}( 265 struct u_trace *ut 266% if trace.need_cs_param: 267 , void *cs 268% endif 269% for arg in trace.args: 270 , ${arg.type} ${arg.var} 271% endfor 272) { 273 enum u_trace_type enabled_traces = p_atomic_read_relaxed(&ut->utctx->enabled_traces); 274 if (!unlikely(enabled_traces != 0 && 275 ${trace.enabled_expr(trace_toggle_name)})) 276 return; 277 __trace_${trace_name}( 278 ut 279 , enabled_traces 280% if trace.need_cs_param: 281 , cs 282% endif 283% for arg in trace.args: 284 , ${arg.var} 285% endfor 286 ); 287} 288% endfor 289 290#ifdef __cplusplus 291} 292#endif 293 294#endif /* ${guard_name} */ 295""" 296 297src_template = """\ 298/* Copyright (C) 2020 Google, Inc. 299 * 300 * Permission is hereby granted, free of charge, to any person obtaining a 301 * copy of this software and associated documentation files (the "Software"), 302 * to deal in the Software without restriction, including without limitation 303 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 304 * and/or sell copies of the Software, and to permit persons to whom the 305 * Software is furnished to do so, subject to the following conditions: 306 * 307 * The above copyright notice and this permission notice (including the next 308 * paragraph) shall be included in all copies or substantial portions of the 309 * Software. 310 * 311 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 312 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 313 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 314 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 315 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 316 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 317 * IN THE SOFTWARE. 318 */ 319 320#include "${hdr}" 321 322% for header in HEADERS: 323#include "${header.hdr}" 324% endfor 325 326#define __NEEDS_TRACE_PRIV 327#include "util/u_debug.h" 328#include "util/perf/u_trace_priv.h" 329 330% if trace_toggle_name is not None: 331static const struct debug_control config_control[] = { 332% for toggle_name in TRACEPOINTS_TOGGLES.keys(): 333 { "${toggle_name}", ${trace_toggle_name.upper()}_${toggle_name.upper()}, }, 334% endfor 335 { NULL, 0, }, 336}; 337uint64_t ${trace_toggle_name} = 0; 338 339static void 340${trace_toggle_name}_variable_once(void) 341{ 342 uint64_t default_value = 0 343% for name in trace_toggle_defaults: 344 | ${trace_toggle_name.upper()}_${name.upper()} 345% endfor 346 ; 347 348 ${trace_toggle_name} = 349 parse_enable_string(getenv("${trace_toggle_name.upper()}"), 350 default_value, 351 config_control); 352} 353 354void 355${trace_toggle_name}_config_variable(void) 356{ 357 static once_flag process_${trace_toggle_name}_variable_flag = ONCE_FLAG_INIT; 358 359 call_once(&process_${trace_toggle_name}_variable_flag, 360 ${trace_toggle_name}_variable_once); 361} 362% endif 363 364% for index, (trace_name, trace) in enumerate(TRACEPOINTS.items()): 365/* 366 * ${trace_name} 367 */ 368 % if trace.can_generate_print(): 369static void __print_${trace_name}(FILE *out, const void *arg) { 370 const struct trace_${trace_name} *__entry = 371 (const struct trace_${trace_name} *)arg; 372 % if trace.tp_print is not None: 373 fprintf(out, "${trace.tp_print[0]}\\n" 374 % for arg in trace.tp_print[1:]: 375 , ${arg} 376 % endfor 377 % else: 378 fprintf(out, "" 379 % for arg in trace.tp_struct: 380 "${arg.name}=${arg.c_format}, " 381 % endfor 382 "\\n" 383 % for arg in trace.tp_struct: 384 % if arg.to_prim_type: 385 ,${arg.to_prim_type.format('__entry->' + arg.name)} 386 % else: 387 ,__entry->${arg.name} 388 % endif 389 % endfor 390 % endif 391 ); 392} 393 394static void __print_json_${trace_name}(FILE *out, const void *arg) { 395 const struct trace_${trace_name} *__entry = 396 (const struct trace_${trace_name} *)arg; 397 % if trace.tp_print is not None: 398 fprintf(out, "\\"unstructured\\": \\"${trace.tp_print[0]}\\"" 399 % for arg in trace.tp_print[1:]: 400 , ${arg} 401 % endfor 402 % else: 403 fprintf(out, "" 404 % for arg in trace.tp_struct: 405 "\\"${arg.name}\\": \\"${arg.c_format}\\"" 406 % if arg != trace.tp_struct[-1]: 407 ", " 408 % endif 409 % endfor 410 % for arg in trace.tp_struct: 411 % if arg.to_prim_type: 412 ,${arg.to_prim_type.format('__entry->' + arg.name)} 413 % else: 414 ,__entry->${arg.name} 415 % endif 416 % endfor 417 % endif 418 ); 419} 420 421 % else: 422#define __print_${trace_name} NULL 423#define __print_json_${trace_name} NULL 424 % endif 425 % if trace.tp_markers is not None: 426 427__attribute__((format(printf, 3, 4))) void ${trace.tp_markers}(struct u_trace_context *utctx, void *, const char *, ...); 428 429static void __emit_label_${trace_name}(struct u_trace_context *utctx, void *cs, struct trace_${trace_name} *entry) { 430 ${trace.tp_markers}(utctx, cs, "${trace_name}(" 431 % for idx,arg in enumerate(trace.tp_struct): 432 "${"," if idx != 0 else ""}${arg.name}=${arg.c_format}" 433 % endfor 434 ")" 435 % for arg in trace.tp_struct: 436 % if arg.to_prim_type: 437 ,${arg.to_prim_type.format('entry->' + arg.name)} 438 % else: 439 ,entry->${arg.name} 440 % endif 441 % endfor 442 ); 443} 444 445 % endif 446static const struct u_tracepoint __tp_${trace_name} = { 447 ALIGN_POT(sizeof(struct trace_${trace_name}), 8), /* keep size 64b aligned */ 448 "${trace_name}", 449 ${"true" if trace.end_of_pipe else "false"}, 450 ${index}, 451 __print_${trace_name}, 452 __print_json_${trace_name}, 453 % if trace.tp_perfetto is not None: 454#ifdef HAVE_PERFETTO 455 (void (*)(void *pctx, uint64_t, uint16_t, const void *, const void *))${trace.tp_perfetto}, 456#endif 457 % endif 458}; 459void __trace_${trace_name}( 460 struct u_trace *ut 461 , enum u_trace_type enabled_traces 462 % if trace.need_cs_param: 463 , void *cs 464 % endif 465 % for arg in trace.args: 466 , ${arg.type} ${arg.var} 467 % endfor 468) { 469 struct trace_${trace_name} entry; 470 UNUSED struct trace_${trace_name} *__entry = 471 enabled_traces & U_TRACE_TYPE_REQUIRE_QUEUING ? 472 % if trace.has_variable_arg: 473 (struct trace_${trace_name} *)u_trace_appendv(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name}, 474 0 475 % for arg in trace.tp_struct: 476 % if arg.length_arg is not None: 477 + ${arg.length_arg} 478 % endif 479 % endfor 480 ) : 481 % else: 482 (struct trace_${trace_name} *)u_trace_append(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name}) : 483 % endif 484 &entry; 485 % for arg in trace.tp_struct: 486 % if arg.length_arg is None: 487 __entry->${arg.name} = ${arg.var}; 488 % else: 489 ${arg.copy_func}(__entry->${arg.name}, ${arg.var}, ${arg.length_arg}); 490 % endif 491 % endfor 492 % if trace.tp_markers is not None: 493 if (enabled_traces & U_TRACE_TYPE_MARKERS) 494 __emit_label_${trace_name}(ut->utctx, cs, __entry); 495 % endif 496} 497 498% endfor 499""" 500 501def utrace_generate(cpath, hpath, ctx_param, trace_toggle_name=None, 502 trace_toggle_defaults=[]): 503 """Parameters: 504 505 - cpath: c file to generate. 506 - hpath: h file to generate. 507 - ctx_param: type of the first parameter to the perfetto vfuncs. 508 - trace_toggle_name: (optional) name of the environment variable 509 enabling/disabling tracepoints. 510 - trace_toggle_defaults: (optional) list of tracepoints enabled by default. 511 """ 512 if cpath is not None: 513 hdr = os.path.basename(cpath).rsplit('.', 1)[0] + '.h' 514 with open(cpath, 'w', encoding='utf-8') as f: 515 f.write(Template(src_template).render( 516 hdr=hdr, 517 ctx_param=ctx_param, 518 trace_toggle_name=trace_toggle_name, 519 trace_toggle_defaults=trace_toggle_defaults, 520 HEADERS=[h for h in HEADERS if h.scope & HeaderScope.SOURCE], 521 TRACEPOINTS=TRACEPOINTS, 522 TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES)) 523 524 if hpath is not None: 525 hdr = os.path.basename(hpath) 526 with open(hpath, 'w', encoding='utf-8') as f: 527 f.write(Template(hdr_template).render( 528 hdrname=hdr.rstrip('.h').upper(), 529 ctx_param=ctx_param, 530 trace_toggle_name=trace_toggle_name, 531 HEADERS=[h for h in HEADERS if h.scope & HeaderScope.HEADER], 532 FORWARD_DECLS=FORWARD_DECLS, 533 TRACEPOINTS=TRACEPOINTS, 534 TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES)) 535 536 537perfetto_utils_hdr_template = """\ 538/* 539 * Copyright © 2021 Igalia S.L. 540 * 541 * Permission is hereby granted, free of charge, to any person obtaining a 542 * copy of this software and associated documentation files (the "Software"), 543 * to deal in the Software without restriction, including without limitation 544 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 545 * and/or sell copies of the Software, and to permit persons to whom the 546 * Software is furnished to do so, subject to the following conditions: 547 * 548 * The above copyright notice and this permission notice (including the next 549 * paragraph) shall be included in all copies or substantial portions of the 550 * Software. 551 * 552 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 553 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 554 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 555 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 556 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 557 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 558 * SOFTWARE. 559 */ 560 561<% guard_name = '_' + hdrname + '_H' %> 562#ifndef ${guard_name} 563#define ${guard_name} 564 565#include <perfetto.h> 566 567% for header in HEADERS: 568#include "${header.hdr}" 569% endfor 570 571UNUSED static const char *${basename}_names[] = { 572% for trace_name, trace in TRACEPOINTS.items(): 573 "${trace_name}", 574% endfor 575}; 576 577% for trace_name, trace in TRACEPOINTS.items(): 578static void UNUSED 579trace_payload_as_extra_${trace_name}(perfetto::protos::pbzero::GpuRenderStageEvent *event, 580 const struct trace_${trace_name} *payload) 581{ 582 % if all([trace.tp_perfetto, trace.tp_struct]) and len(trace.tp_struct) > 0: 583 char buf[128]; 584 585 % for arg in trace.tp_struct: 586 { 587 auto data = event->add_extra_data(); 588 data->set_name("${arg.name}"); 589 590 % if arg.to_prim_type: 591 sprintf(buf, "${arg.c_format}", ${arg.to_prim_type.format('payload->' + arg.name)}); 592 % else: 593 sprintf(buf, "${arg.c_format}", payload->${arg.name}); 594 % endif 595 596 data->set_value(buf); 597 } 598 % endfor 599 600 % endif 601} 602% endfor 603 604#endif /* ${guard_name} */ 605""" 606 607def utrace_generate_perfetto_utils(hpath,basename="tracepoint"): 608 if hpath is not None: 609 hdr = os.path.basename(hpath) 610 with open(hpath, 'w', encoding='utf-8') as f: 611 f.write(Template(perfetto_utils_hdr_template).render( 612 basename=basename, 613 hdrname=hdr.rstrip('.h').upper(), 614 HEADERS=[h for h in HEADERS if h.scope & HeaderScope.PERFETTO], 615 TRACEPOINTS=TRACEPOINTS)) 616