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