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 const void *flush_data, 250 const struct trace_${trace_name} *payload); 251#endif 252% endif 253void __trace_${trace_name}( 254 struct u_trace *ut 255 , enum u_trace_type enabled_traces 256% if trace.need_cs_param: 257 , void *cs 258% endif 259% for arg in trace.args: 260 , ${arg.type} ${arg.var} 261% endfor 262); 263static ALWAYS_INLINE void trace_${trace_name}( 264 struct u_trace *ut 265% if trace.need_cs_param: 266 , void *cs 267% endif 268% for arg in trace.args: 269 , ${arg.type} ${arg.var} 270% endfor 271) { 272 enum u_trace_type enabled_traces = p_atomic_read_relaxed(&ut->utctx->enabled_traces); 273 if (!unlikely(enabled_traces != 0 && 274 ${trace.enabled_expr(trace_toggle_name)})) 275 return; 276 __trace_${trace_name}( 277 ut 278 , enabled_traces 279% if trace.need_cs_param: 280 , cs 281% endif 282% for arg in trace.args: 283 , ${arg.var} 284% endfor 285 ); 286} 287% endfor 288 289#ifdef __cplusplus 290} 291#endif 292 293#endif /* ${guard_name} */ 294""" 295 296src_template = """\ 297/* Copyright (C) 2020 Google, Inc. 298 * 299 * Permission is hereby granted, free of charge, to any person obtaining a 300 * copy of this software and associated documentation files (the "Software"), 301 * to deal in the Software without restriction, including without limitation 302 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 303 * and/or sell copies of the Software, and to permit persons to whom the 304 * Software is furnished to do so, subject to the following conditions: 305 * 306 * The above copyright notice and this permission notice (including the next 307 * paragraph) shall be included in all copies or substantial portions of the 308 * Software. 309 * 310 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 311 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 312 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 313 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 314 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 315 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 316 * IN THE SOFTWARE. 317 */ 318 319#include "${hdr}" 320 321% for header in HEADERS: 322#include "${header.hdr}" 323% endfor 324 325#define __NEEDS_TRACE_PRIV 326#include "util/u_debug.h" 327#include "util/perf/u_trace_priv.h" 328 329% if trace_toggle_name is not None: 330static const struct debug_control config_control[] = { 331% for toggle_name in TRACEPOINTS_TOGGLES.keys(): 332 { "${toggle_name}", ${trace_toggle_name.upper()}_${toggle_name.upper()}, }, 333% endfor 334 { NULL, 0, }, 335}; 336uint64_t ${trace_toggle_name} = 0; 337 338static void 339${trace_toggle_name}_variable_once(void) 340{ 341 uint64_t default_value = 0 342% for name in trace_toggle_defaults: 343 | ${trace_toggle_name.upper()}_${name.upper()} 344% endfor 345 ; 346 347 ${trace_toggle_name} = 348 parse_enable_string(getenv("${trace_toggle_name.upper()}"), 349 default_value, 350 config_control); 351} 352 353void 354${trace_toggle_name}_config_variable(void) 355{ 356 static once_flag process_${trace_toggle_name}_variable_flag = ONCE_FLAG_INIT; 357 358 call_once(&process_${trace_toggle_name}_variable_flag, 359 ${trace_toggle_name}_variable_once); 360} 361% endif 362 363% for trace_name, trace in TRACEPOINTS.items(): 364/* 365 * ${trace_name} 366 */ 367 % if trace.can_generate_print(): 368static void __print_${trace_name}(FILE *out, const void *arg) { 369 const struct trace_${trace_name} *__entry = 370 (const struct trace_${trace_name} *)arg; 371 % if trace.tp_print is not None: 372 fprintf(out, "${trace.tp_print[0]}\\n" 373 % for arg in trace.tp_print[1:]: 374 , ${arg} 375 % endfor 376 % else: 377 fprintf(out, "" 378 % for arg in trace.tp_struct: 379 "${arg.name}=${arg.c_format}, " 380 % endfor 381 "\\n" 382 % for arg in trace.tp_struct: 383 % if arg.to_prim_type: 384 ,${arg.to_prim_type.format('__entry->' + arg.name)} 385 % else: 386 ,__entry->${arg.name} 387 % endif 388 % endfor 389 % endif 390 ); 391} 392 393static void __print_json_${trace_name}(FILE *out, const void *arg) { 394 const struct trace_${trace_name} *__entry = 395 (const struct trace_${trace_name} *)arg; 396 % if trace.tp_print is not None: 397 fprintf(out, "\\"unstructured\\": \\"${trace.tp_print[0]}\\"" 398 % for arg in trace.tp_print[1:]: 399 , ${arg} 400 % endfor 401 % else: 402 fprintf(out, "" 403 % for arg in trace.tp_struct: 404 "\\"${arg.name}\\": \\"${arg.c_format}\\"" 405 % if arg != trace.tp_struct[-1]: 406 ", " 407 % endif 408 % endfor 409 % for arg in trace.tp_struct: 410 % if arg.to_prim_type: 411 ,${arg.to_prim_type.format('__entry->' + arg.name)} 412 % else: 413 ,__entry->${arg.name} 414 % endif 415 % endfor 416 % endif 417 ); 418} 419 420 % else: 421#define __print_${trace_name} NULL 422#define __print_json_${trace_name} NULL 423 % endif 424 % if trace.tp_markers is not None: 425 426__attribute__((format(printf, 3, 4))) void ${trace.tp_markers}(struct u_trace_context *utctx, void *, const char *, ...); 427 428static void __emit_label_${trace_name}(struct u_trace_context *utctx, void *cs, struct trace_${trace_name} *entry) { 429 ${trace.tp_markers}(utctx, cs, "${trace_name}(" 430 % for idx,arg in enumerate(trace.tp_struct): 431 "${"," if idx != 0 else ""}${arg.name}=${arg.c_format}" 432 % endfor 433 ")" 434 % for arg in trace.tp_struct: 435 % if arg.to_prim_type: 436 ,${arg.to_prim_type.format('entry->' + arg.name)} 437 % else: 438 ,entry->${arg.name} 439 % endif 440 % endfor 441 ); 442} 443 444 % endif 445static const struct u_tracepoint __tp_${trace_name} = { 446 ALIGN_POT(sizeof(struct trace_${trace_name}), 8), /* keep size 64b aligned */ 447 "${trace_name}", 448 ${"true" if trace.end_of_pipe else "false"}, 449 __print_${trace_name}, 450 __print_json_${trace_name}, 451 % if trace.tp_perfetto is not None: 452#ifdef HAVE_PERFETTO 453 (void (*)(void *pctx, uint64_t, const void *, const void *))${trace.tp_perfetto}, 454#endif 455 % endif 456}; 457void __trace_${trace_name}( 458 struct u_trace *ut 459 , enum u_trace_type enabled_traces 460 % if trace.need_cs_param: 461 , void *cs 462 % endif 463 % for arg in trace.args: 464 , ${arg.type} ${arg.var} 465 % endfor 466) { 467 struct trace_${trace_name} entry; 468 UNUSED struct trace_${trace_name} *__entry = 469 enabled_traces & U_TRACE_TYPE_REQUIRE_QUEUING ? 470 % if trace.has_variable_arg: 471 (struct trace_${trace_name} *)u_trace_appendv(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name}, 472 0 473 % for arg in trace.tp_struct: 474 % if arg.length_arg is not None: 475 + ${arg.length_arg} 476 % endif 477 % endfor 478 ) : 479 % else: 480 (struct trace_${trace_name} *)u_trace_append(ut, ${"cs," if trace.need_cs_param else "NULL,"} &__tp_${trace_name}) : 481 % endif 482 &entry; 483 % for arg in trace.tp_struct: 484 % if arg.length_arg is None: 485 __entry->${arg.name} = ${arg.var}; 486 % else: 487 ${arg.copy_func}(__entry->${arg.name}, ${arg.var}, ${arg.length_arg}); 488 % endif 489 % endfor 490 % if trace.tp_markers is not None: 491 if (enabled_traces & U_TRACE_TYPE_MARKERS) 492 __emit_label_${trace_name}(ut->utctx, cs, __entry); 493 % endif 494} 495 496% endfor 497""" 498 499def utrace_generate(cpath, hpath, ctx_param, trace_toggle_name=None, 500 trace_toggle_defaults=[]): 501 """Parameters: 502 503 - cpath: c file to generate. 504 - hpath: h file to generate. 505 - ctx_param: type of the first parameter to the perfetto vfuncs. 506 - trace_toggle_name: (optional) name of the environment variable 507 enabling/disabling tracepoints. 508 - trace_toggle_defaults: (optional) list of tracepoints enabled by default. 509 """ 510 if cpath is not None: 511 hdr = os.path.basename(cpath).rsplit('.', 1)[0] + '.h' 512 with open(cpath, 'w') as f: 513 f.write(Template(src_template).render( 514 hdr=hdr, 515 ctx_param=ctx_param, 516 trace_toggle_name=trace_toggle_name, 517 trace_toggle_defaults=trace_toggle_defaults, 518 HEADERS=[h for h in HEADERS if h.scope & HeaderScope.SOURCE], 519 TRACEPOINTS=TRACEPOINTS, 520 TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES)) 521 522 if hpath is not None: 523 hdr = os.path.basename(hpath) 524 with open(hpath, 'w') as f: 525 f.write(Template(hdr_template).render( 526 hdrname=hdr.rstrip('.h').upper(), 527 ctx_param=ctx_param, 528 trace_toggle_name=trace_toggle_name, 529 HEADERS=[h for h in HEADERS if h.scope & HeaderScope.HEADER], 530 FORWARD_DECLS=FORWARD_DECLS, 531 TRACEPOINTS=TRACEPOINTS, 532 TRACEPOINTS_TOGGLES=TRACEPOINTS_TOGGLES)) 533 534 535perfetto_utils_hdr_template = """\ 536/* 537 * Copyright © 2021 Igalia S.L. 538 * 539 * Permission is hereby granted, free of charge, to any person obtaining a 540 * copy of this software and associated documentation files (the "Software"), 541 * to deal in the Software without restriction, including without limitation 542 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 543 * and/or sell copies of the Software, and to permit persons to whom the 544 * Software is furnished to do so, subject to the following conditions: 545 * 546 * The above copyright notice and this permission notice (including the next 547 * paragraph) shall be included in all copies or substantial portions of the 548 * Software. 549 * 550 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 551 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 552 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 553 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 554 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 555 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 556 * SOFTWARE. 557 */ 558 559<% guard_name = '_' + hdrname + '_H' %> 560#ifndef ${guard_name} 561#define ${guard_name} 562 563#include <perfetto.h> 564 565% for header in HEADERS: 566#include "${header.hdr}" 567% endfor 568 569% for trace_name, trace in TRACEPOINTS.items(): 570static void UNUSED 571trace_payload_as_extra_${trace_name}(perfetto::protos::pbzero::GpuRenderStageEvent *event, 572 const struct trace_${trace_name} *payload) 573{ 574 % if all([trace.tp_perfetto, trace.tp_struct]) and len(trace.tp_struct) > 0: 575 char buf[128]; 576 577 % for arg in trace.tp_struct: 578 { 579 auto data = event->add_extra_data(); 580 data->set_name("${arg.name}"); 581 582 % if arg.to_prim_type: 583 sprintf(buf, "${arg.c_format}", ${arg.to_prim_type.format('payload->' + arg.name)}); 584 % else: 585 sprintf(buf, "${arg.c_format}", payload->${arg.name}); 586 % endif 587 588 data->set_value(buf); 589 } 590 % endfor 591 592 % endif 593} 594% endfor 595 596#endif /* ${guard_name} */ 597""" 598 599def utrace_generate_perfetto_utils(hpath): 600 if hpath is not None: 601 hdr = os.path.basename(hpath) 602 with open(hpath, 'wb') as f: 603 f.write(Template(perfetto_utils_hdr_template, output_encoding='utf-8').render( 604 hdrname=hdr.rstrip('.h').upper(), 605 HEADERS=[h for h in HEADERS if h.scope & HeaderScope.PERFETTO], 606 TRACEPOINTS=TRACEPOINTS)) 607