• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2015-2017 Intel Corporation
2#
3# Permission is hereby granted, free of charge, to any person obtaining a
4# copy of this software and associated documentation files (the "Software"),
5# to deal in the Software without restriction, including without limitation
6# the rights to use, copy, modify, merge, publish, distribute, sublicense,
7# and/or sell copies of the Software, and to permit persons to whom the
8# Software is furnished to do so, subject to the following conditions:
9#
10# The above copyright notice and this permission notice (including the next
11# paragraph) shall be included in all copies or substantial portions of the
12# Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20# IN THE SOFTWARE.
21
22import argparse
23import builtins
24import collections
25import os
26import re
27import sys
28import textwrap
29
30import xml.etree.ElementTree as et
31
32hashed_funcs = {}
33
34c_file = None
35_c_indent = 0
36
37def c(*args):
38    code = ' '.join(map(str,args))
39    for line in code.splitlines():
40        text = ''.rjust(_c_indent) + line
41        c_file.write(text.rstrip() + "\n")
42
43# indented, but no trailing newline...
44def c_line_start(code):
45    c_file.write(''.rjust(_c_indent) + code)
46def c_raw(code):
47    c_file.write(code)
48
49def c_indent(n):
50    global _c_indent
51    _c_indent = _c_indent + n
52def c_outdent(n):
53    global _c_indent
54    _c_indent = _c_indent - n
55
56header_file = None
57_h_indent = 0
58
59def h(*args):
60    code = ' '.join(map(str,args))
61    for line in code.splitlines():
62        text = ''.rjust(_h_indent) + line
63        header_file.write(text.rstrip() + "\n")
64
65def h_indent(n):
66    global _c_indent
67    _h_indent = _h_indent + n
68def h_outdent(n):
69    global _c_indent
70    _h_indent = _h_indent - n
71
72
73def emit_fadd(tmp_id, args):
74    c("double tmp{0} = {1} + {2};".format(tmp_id, args[1], args[0]))
75    return tmp_id + 1
76
77# Be careful to check for divide by zero...
78def emit_fdiv(tmp_id, args):
79    c("double tmp{0} = {1};".format(tmp_id, args[1]))
80    c("double tmp{0} = {1};".format(tmp_id + 1, args[0]))
81    c("double tmp{0} = tmp{1} ? tmp{2} / tmp{1} : 0;".format(tmp_id + 2, tmp_id + 1, tmp_id))
82    return tmp_id + 3
83
84def emit_fmax(tmp_id, args):
85    c("double tmp{0} = {1};".format(tmp_id, args[1]))
86    c("double tmp{0} = {1};".format(tmp_id + 1, args[0]))
87    c("double tmp{0} = MAX(tmp{1}, tmp{2});".format(tmp_id + 2, tmp_id, tmp_id + 1))
88    return tmp_id + 3
89
90def emit_fmul(tmp_id, args):
91    c("double tmp{0} = {1} * {2};".format(tmp_id, args[1], args[0]))
92    return tmp_id + 1
93
94def emit_fsub(tmp_id, args):
95    c("double tmp{0} = {1} - {2};".format(tmp_id, args[1], args[0]))
96    return tmp_id + 1
97
98def emit_read(tmp_id, args):
99    type = args[1].lower()
100    c("uint64_t tmp{0} = results->accumulator[query->{1}_offset + {2}];".format(tmp_id, type, args[0]))
101    return tmp_id + 1
102
103def emit_uadd(tmp_id, args):
104    c("uint64_t tmp{0} = {1} + {2};".format(tmp_id, args[1], args[0]))
105    return tmp_id + 1
106
107# Be careful to check for divide by zero...
108def emit_udiv(tmp_id, args):
109    c("uint64_t tmp{0} = {1};".format(tmp_id, args[1]))
110    c("uint64_t tmp{0} = {1};".format(tmp_id + 1, args[0]))
111    if args[0].isdigit():
112        assert int(args[0]) > 0
113        c("uint64_t tmp{0} = tmp{2} / tmp{1};".format(tmp_id + 2, tmp_id + 1, tmp_id))
114    else:
115        c("uint64_t tmp{0} = tmp{1} ? tmp{2} / tmp{1} : 0;".format(tmp_id + 2, tmp_id + 1, tmp_id))
116    return tmp_id + 3
117
118def emit_umul(tmp_id, args):
119    c("uint64_t tmp{0} = {1} * {2};".format(tmp_id, args[1], args[0]))
120    return tmp_id + 1
121
122def emit_usub(tmp_id, args):
123    c("uint64_t tmp{0} = {1} - {2};".format(tmp_id, args[1], args[0]))
124    return tmp_id + 1
125
126def emit_umin(tmp_id, args):
127    c("uint64_t tmp{0} = MIN({1}, {2});".format(tmp_id, args[1], args[0]))
128    return tmp_id + 1
129
130def emit_lshft(tmp_id, args):
131    c("uint64_t tmp{0} = {1} << {2};".format(tmp_id, args[1], args[0]))
132    return tmp_id + 1
133
134def emit_rshft(tmp_id, args):
135    c("uint64_t tmp{0} = {1} >> {2};".format(tmp_id, args[1], args[0]))
136    return tmp_id + 1
137
138def emit_and(tmp_id, args):
139    c("uint64_t tmp{0} = {1} & {2};".format(tmp_id, args[1], args[0]))
140    return tmp_id + 1
141
142def emit_ulte(tmp_id, args):
143    c("uint64_t tmp{0} = {1} <= {2};".format(tmp_id, args[1], args[0]))
144    return tmp_id + 1
145
146def emit_ult(tmp_id, args):
147    c("uint64_t tmp{0} = {1} < {2};".format(tmp_id, args[1], args[0]))
148    return tmp_id + 1
149
150def emit_ugte(tmp_id, args):
151    c("uint64_t tmp{0} = {1} >= {2};".format(tmp_id, args[1], args[0]))
152    return tmp_id + 1
153
154def emit_ugt(tmp_id, args):
155    c("uint64_t tmp{0} = {1} > {2};".format(tmp_id, args[1], args[0]))
156    return tmp_id + 1
157
158ops = {}
159#             (n operands, emitter)
160ops["FADD"] = (2, emit_fadd)
161ops["FDIV"] = (2, emit_fdiv)
162ops["FMAX"] = (2, emit_fmax)
163ops["FMUL"] = (2, emit_fmul)
164ops["FSUB"] = (2, emit_fsub)
165ops["READ"] = (2, emit_read)
166ops["UADD"] = (2, emit_uadd)
167ops["UDIV"] = (2, emit_udiv)
168ops["UMUL"] = (2, emit_umul)
169ops["USUB"] = (2, emit_usub)
170ops["UMIN"] = (2, emit_umin)
171ops["<<"]   = (2, emit_lshft)
172ops[">>"]   = (2, emit_rshft)
173ops["AND"]  = (2, emit_and)
174ops["UGTE"] = (2, emit_ugte)
175ops["UGT"]  = (2, emit_ugt)
176ops["ULTE"] = (2, emit_ulte)
177ops["ULT"]  = (2, emit_ult)
178
179
180def brkt(subexp):
181    if " " in subexp:
182        return "(" + subexp + ")"
183    else:
184        return subexp
185
186def splice_bitwise_and(args):
187    return brkt(args[1]) + " & " + brkt(args[0])
188
189def splice_bitwise_or(args):
190    return brkt(args[1]) + " | " + brkt(args[0])
191
192def splice_logical_and(args):
193    return brkt(args[1]) + " && " + brkt(args[0])
194
195def splice_umul(args):
196    return brkt(args[1]) + " * " + brkt(args[0])
197
198def splice_ult(args):
199    return brkt(args[1]) + " < " + brkt(args[0])
200
201def splice_ugte(args):
202    return brkt(args[1]) + " >= " + brkt(args[0])
203
204def splice_ulte(args):
205    return brkt(args[1]) + " <= " + brkt(args[0])
206
207def splice_ugt(args):
208    return brkt(args[1]) + " > " + brkt(args[0])
209
210def splice_lshft(args):
211    return brkt(args[1]) + " << " + brkt(args[0])
212
213def splice_equal(args):
214    return brkt(args[1]) + " == " + brkt(args[0])
215
216exp_ops = {}
217#                 (n operands, splicer)
218exp_ops["AND"]  = (2, splice_bitwise_and)
219exp_ops["OR"]   = (2, splice_bitwise_or)
220exp_ops["UGTE"] = (2, splice_ugte)
221exp_ops["ULT"]  = (2, splice_ult)
222exp_ops["&&"]   = (2, splice_logical_and)
223exp_ops["UMUL"] = (2, splice_umul)
224exp_ops["<<"]   = (2, splice_lshft)
225exp_ops["=="]   = (2, splice_equal)
226
227
228hw_vars = {}
229hw_vars["$EuCoresTotalCount"] = "perf->sys_vars.n_eus"
230hw_vars["$VectorEngineTotalCount"] = "perf->sys_vars.n_eus"
231hw_vars["$EuSlicesTotalCount"] = "perf->sys_vars.n_eu_slices"
232hw_vars["$EuSubslicesTotalCount"] = "perf->sys_vars.n_eu_sub_slices"
233hw_vars["$XeCoreTotalCount"] = "perf->sys_vars.n_eu_sub_slices"
234hw_vars["$EuDualSubslicesTotalCount"] = "perf->sys_vars.n_eu_sub_slices"
235hw_vars["$EuDualSubslicesSlice0123Count"] = "perf->sys_vars.n_eu_slice0123"
236hw_vars["$EuThreadsCount"] = "perf->devinfo.num_thread_per_eu"
237hw_vars["$VectorEngineThreadsCount"] = "perf->devinfo.num_thread_per_eu"
238hw_vars["$SliceMask"] = "perf->sys_vars.slice_mask"
239hw_vars["$SliceTotalCount"] = "perf->sys_vars.n_eu_slices"
240# subslice_mask is interchangeable with subslice/dual-subslice since Gfx12+
241# only has dual subslices which can be assimilated with 16EUs subslices.
242hw_vars["$SubsliceMask"] = "perf->sys_vars.subslice_mask"
243hw_vars["$DualSubsliceMask"] = "perf->sys_vars.subslice_mask"
244hw_vars["$XeCoreMask"] = "perf->sys_vars.subslice_mask"
245hw_vars["$GpuTimestampFrequency"] = "perf->devinfo.timestamp_frequency"
246hw_vars["$GpuMinFrequency"] = "perf->sys_vars.gt_min_freq"
247hw_vars["$GpuMaxFrequency"] = "perf->sys_vars.gt_max_freq"
248hw_vars["$SkuRevisionId"] = "perf->devinfo.revision"
249hw_vars["$QueryMode"] = "perf->sys_vars.query_mode"
250
251def resolve_variable(name, set, allow_counters):
252    if name in hw_vars:
253        return hw_vars[name]
254    m = re.search(r'\$GtSlice([0-9]+)$', name)
255    if m:
256        return 'intel_device_info_slice_available(&perf->devinfo, {0})'.format(m.group(1))
257    m = re.search(r'\$GtSlice([0-9]+)XeCore([0-9]+)$', name)
258    if m:
259        return 'intel_device_info_subslice_available(&perf->devinfo, {0}, {1})'.format(m.group(1), m.group(2))
260    if allow_counters and name in set.counter_vars:
261        return set.read_funcs[name[1:]] + "(perf, query, results)"
262    return None
263
264def output_rpn_equation_code(set, counter, equation):
265    c("/* RPN equation: " + equation + " */")
266    tokens = equation.split()
267    stack = []
268    tmp_id = 0
269    tmp = None
270
271    for token in tokens:
272        stack.append(token)
273        while stack and stack[-1] in ops:
274            op = stack.pop()
275            argc, callback = ops[op]
276            args = []
277            for i in range(0, argc):
278                operand = stack.pop()
279                if operand[0] == "$":
280                    resolved_variable = resolve_variable(operand, set, True)
281                    if resolved_variable == None:
282                        raise Exception("Failed to resolve variable " + operand + " in equation " + equation + " for " + set.name + " :: " + counter.get('name'));
283                    operand = resolved_variable
284                args.append(operand)
285
286            tmp_id = callback(tmp_id, args)
287
288            tmp = "tmp{0}".format(tmp_id - 1)
289            stack.append(tmp)
290
291    if len(stack) != 1:
292        raise Exception("Spurious empty rpn code for " + set.name + " :: " +
293                counter.get('name') + ".\nThis is probably due to some unhandled RPN function, in the equation \"" +
294                equation + "\"")
295
296    value = stack[-1]
297
298    if value[0] == "$":
299        resolved_variable = resolve_variable(value, set, True)
300        if resolved_variable == None:
301            raise Exception("Failed to resolve variable " + operand + " in equation " + equation + " for " + set.name + " :: " + counter.get('name'));
302        value = resolved_variable
303
304    c("\nreturn " + value + ";")
305
306def splice_rpn_expression(set, counter_name, expression):
307    tokens = expression.split()
308    stack = []
309
310    for token in tokens:
311        stack.append(token)
312        while stack and stack[-1] in exp_ops:
313            op = stack.pop()
314            argc, callback = exp_ops[op]
315            args = []
316            for i in range(0, argc):
317                operand = stack.pop()
318                if operand[0] == "$":
319                    resolved_variable = resolve_variable(operand, set, False)
320                    if resolved_variable == None:
321                        raise Exception("Failed to resolve variable " + operand + " in expression " + expression + " for " + set.name + " :: " + counter_name)
322                    operand = resolved_variable
323                args.append(operand)
324
325            subexp = callback(args)
326
327            stack.append(subexp)
328
329    if len(stack) != 1:
330        raise Exception("Spurious empty rpn expression for " + set.name + " :: " +
331                counter_name + ".\nThis is probably due to some unhandled RPN operation, in the expression \"" +
332                expression + "\"")
333
334    value = stack[-1]
335
336    if value[0] == "$":
337        resolved_variable = resolve_variable(value, set, False)
338        if resolved_variable == None:
339            raise Exception("Failed to resolve variable " + operand + " in expression " + expression + " for " + set.name + " :: " + counter_name)
340        value = resolved_variable
341
342    return value
343
344def output_counter_read(gen, set, counter):
345    c("\n")
346    c("/* {0} :: {1} */".format(set.name, counter.get('name')))
347
348    if counter.read_hash in hashed_funcs:
349        c("#define %s \\" % counter.read_sym)
350        c_indent(3)
351        c("%s" % hashed_funcs[counter.read_hash])
352        c_outdent(3)
353    else:
354        ret_type = counter.get('data_type')
355        if ret_type == "uint64":
356            ret_type = "uint64_t"
357
358        read_eq = counter.get('equation')
359
360        c("static " + ret_type)
361        c(counter.read_sym + "(UNUSED struct intel_perf_config *perf,\n")
362        c_indent(len(counter.read_sym) + 1)
363        c("const struct intel_perf_query_info *query,\n")
364        c("const struct intel_perf_query_result *results)\n")
365        c_outdent(len(counter.read_sym) + 1)
366
367        c("{")
368        c_indent(3)
369        output_rpn_equation_code(set, counter, read_eq)
370        c_outdent(3)
371        c("}")
372
373        hashed_funcs[counter.read_hash] = counter.read_sym
374
375
376def output_counter_max(gen, set, counter):
377    max_eq = counter.get('max_equation')
378
379    if not counter.has_custom_max_func():
380        return
381
382    c("\n")
383    c("/* {0} :: {1} */".format(set.name, counter.get('name')))
384
385    if counter.max_hash in hashed_funcs:
386        c("#define %s \\" % counter.max_sym)
387        c_indent(3)
388        c("%s" % hashed_funcs[counter.max_hash])
389        c_outdent(3)
390    else:
391        ret_type = counter.get('data_type')
392        if ret_type == "uint64":
393            ret_type = "uint64_t"
394
395        c("static " + ret_type)
396        c(counter.max_sym + "(struct intel_perf_config *perf,\n")
397        c_indent(len(counter.read_sym) + 1)
398        c("const struct intel_perf_query_info *query,\n")
399        c("const struct intel_perf_query_result *results)\n")
400        c_outdent(len(counter.read_sym) + 1)
401        c("{")
402        c_indent(3)
403        output_rpn_equation_code(set, counter, max_eq)
404        c_outdent(3)
405        c("}")
406
407        hashed_funcs[counter.max_hash] = counter.max_sym
408
409
410c_type_sizes = { "uint32_t": 4, "uint64_t": 8, "float": 4, "double": 8, "bool": 4 }
411def sizeof(c_type):
412    return c_type_sizes[c_type]
413
414def pot_align(base, pot_alignment):
415    return (base + pot_alignment - 1) & ~(pot_alignment - 1);
416
417semantic_type_map = {
418    "duration": "raw",
419    "ratio": "event"
420    }
421
422def output_availability(set, availability, counter_name):
423    expression = splice_rpn_expression(set, counter_name, availability)
424    lines = expression.split(' && ')
425    n_lines = len(lines)
426    if n_lines == 1:
427        c("if (" + lines[0] + ") {")
428    else:
429        c("if (" + lines[0] + " &&")
430        c_indent(4)
431        for i in range(1, (n_lines - 1)):
432            c(lines[i] + " &&")
433        c(lines[(n_lines - 1)] + ") {")
434        c_outdent(4)
435
436
437def output_units(unit):
438    return unit.replace(' ', '_').upper()
439
440
441# should a unit be visible in description?
442units_map = {
443    "bytes" : True,
444    "cycles" : True,
445    "eu atomic requests to l3 cache lines" : False,
446    "eu bytes per l3 cache line" : False,
447    "eu requests to l3 cache lines" : False,
448    "eu sends to l3 cache lines" : False,
449    "events" : True,
450    "hz" : True,
451    "messages" : True,
452    "ns" : True,
453    "number" : False,
454    "percent" : True,
455    "pixels" : True,
456    "texels" : True,
457    "threads" : True,
458    "us" : True,
459    "utilization" : False,
460    "gbps" : True,
461    }
462
463
464def desc_units(unit):
465    val = units_map.get(unit)
466    if val is None:
467        raise Exception("Unknown unit: " + unit)
468    if val == False:
469        return ""
470    if unit == 'hz':
471        unit = 'Hz'
472    return "Unit: " + unit + "."
473
474
475counter_key_tuple = collections.namedtuple(
476    'counter_key',
477    [
478        'name',
479        'description',
480        'symbol_name',
481        'mdapi_group',
482        'semantic_type',
483        'data_type',
484        'units',
485    ]
486)
487
488
489def counter_key(counter):
490    return counter_key_tuple._make([counter.get(field) for field in counter_key_tuple._fields])
491
492
493def output_counter_struct(set, counter, idx,
494                          name_to_idx, desc_to_idx,
495                          symbol_name_to_idx, category_to_idx):
496    data_type = counter.data_type
497    data_type_uc = data_type.upper()
498
499    semantic_type = counter.semantic_type
500    if semantic_type in semantic_type_map:
501        semantic_type = semantic_type_map[semantic_type]
502
503    semantic_type_uc = semantic_type.upper()
504
505    c("[" + str(idx) + "] = {\n")
506    c_indent(3)
507    c(".name_idx = " + str(name_to_idx[counter.name]) + ",\n")
508    c(".desc_idx = " + str(desc_to_idx[counter.description + " " + desc_units(counter.units)]) + ",\n")
509    c(".symbol_name_idx = " + str(symbol_name_to_idx[counter.symbol_name]) + ",\n")
510    c(".category_idx = " + str(category_to_idx[counter.mdapi_group]) + ",\n")
511    c(".type = INTEL_PERF_COUNTER_TYPE_" + semantic_type_uc + ",\n")
512    c(".data_type = INTEL_PERF_COUNTER_DATA_TYPE_" + data_type_uc + ",\n")
513    c(".units = INTEL_PERF_COUNTER_UNITS_" + output_units(counter.units) + ",\n")
514    c_outdent(3)
515    c("},\n")
516
517
518def output_counter_report(set, counter, counter_to_idx, current_offset):
519    data_type = counter.get('data_type')
520    data_type_uc = data_type.upper()
521    c_type = data_type
522
523    if "uint" in c_type:
524        c_type = c_type + "_t"
525
526    semantic_type = counter.get('semantic_type')
527    if semantic_type in semantic_type_map:
528        semantic_type = semantic_type_map[semantic_type]
529
530    semantic_type_uc = semantic_type.upper()
531
532    c("\n")
533
534    availability = counter.get('availability')
535    if availability:
536        output_availability(set, availability, counter.get('name'))
537        c_indent(3)
538
539    key = counter_key(counter)
540    idx = str(counter_to_idx[key])
541
542    current_offset = pot_align(current_offset, sizeof(c_type))
543
544    if data_type == 'uint64':
545        c("intel_perf_query_add_counter_uint64(query, " + idx + ", " +
546          str(current_offset) + ", " +
547          set.max_funcs[counter.get('symbol_name')] + "," +
548          set.read_funcs[counter.get('symbol_name')] + ");\n")
549    else:
550        c("intel_perf_query_add_counter_float(query, " + idx + ", " +
551          str(current_offset) + ", " +
552          set.max_funcs[counter.get('symbol_name')] + "," +
553          set.read_funcs[counter.get('symbol_name')] + ");\n")
554
555
556    if availability:
557        c_outdent(3);
558        c("}")
559
560    return current_offset + sizeof(c_type)
561
562
563def str_to_idx_table(strs):
564    sorted_strs = sorted(strs)
565
566    str_to_idx = collections.OrderedDict()
567    str_to_idx[sorted_strs[0]] = 0
568    previous = sorted_strs[0]
569
570    for i in range(1, len(sorted_strs)):
571        str_to_idx[sorted_strs[i]] = str_to_idx[previous] + len(previous) + 1
572        previous = sorted_strs[i]
573
574    return str_to_idx
575
576
577def output_str_table(name: str, str_to_idx):
578    c("\n")
579    c("static const char " + name + "[] = {\n")
580    c_indent(3)
581    c("\n".join(f"/* {idx} */ \"{val}\\0\"" for val, idx in str_to_idx.items()))
582    c_outdent(3)
583    c("};\n")
584
585
586register_types = {
587    'FLEX': 'flex_regs',
588    'NOA': 'mux_regs',
589    'OA': 'b_counter_regs',
590}
591
592def compute_register_lengths(set):
593    register_lengths = {}
594    register_configs = set.findall('register_config')
595    for register_config in register_configs:
596        t = register_types[register_config.get('type')]
597        if t not in register_lengths:
598            register_lengths[t] = len(register_config.findall('register'))
599        else:
600            register_lengths[t] += len(register_config.findall('register'))
601
602    return register_lengths
603
604
605def generate_register_configs(set):
606    register_configs = set.findall('register_config')
607
608    for register_config in register_configs:
609        t = register_types[register_config.get('type')]
610
611        availability = register_config.get('availability')
612        if availability:
613            output_availability(set, availability, register_config.get('type') + ' register config')
614            c_indent(3)
615
616        registers = register_config.findall('register')
617        c("static const struct intel_perf_query_register_prog %s[] = {" % t)
618        c_indent(3)
619        for register in registers:
620            c("{ .reg = %s, .val = %s }," % (register.get('address'), register.get('value')))
621        c_outdent(3)
622        c("};")
623        c("query->config.%s = %s;" % (t, t))
624        c("query->config.n_%s = ARRAY_SIZE(%s);" % (t, t))
625
626        if availability:
627            c_outdent(3)
628            c("}")
629        c("\n")
630
631
632# Wraps a <counter> element from the oa-*.xml files.
633class Counter:
634    def __init__(self, set, xml):
635        self.xml = xml
636        self.set = set
637        self.read_hash = None
638        self.max_hash = None
639
640        self.read_sym = "{0}__{1}__{2}__read".format(self.set.gen.chipset,
641                                                     self.set.underscore_name,
642                                                     self.xml.get('underscore_name'))
643        self.max_sym = self.build_max_sym()
644
645    def get(self, prop):
646        return self.xml.get(prop)
647
648    # Compute the hash of a counter's equation by expanding (including all the
649    # sub-equations it depends on)
650    def compute_hashes(self):
651        if self.read_hash is not None:
652            return
653
654        def replace_token(token):
655            if token[0] != "$":
656                return token
657            if token not in self.set.counter_vars:
658                return token
659            self.set.counter_vars[token].compute_hashes()
660            return self.set.counter_vars[token].read_hash
661
662        read_eq = self.xml.get('equation')
663        self.read_hash = ' '.join(map(replace_token, read_eq.split()))
664
665        max_eq = self.xml.get('max_equation')
666        if max_eq:
667            self.max_hash = ' '.join(map(replace_token, max_eq.split()))
668
669    def has_custom_max_func(self):
670        max_eq = self.xml.get('max_equation')
671        if not max_eq:
672            return False
673
674        try:
675            val = float(max_eq)
676            if val == 100:
677                return False
678        except ValueError:
679            pass
680
681        for token in max_eq.split():
682            if token[0] == '$' and resolve_variable(token, self.set, True) == None:
683                print("unresolved token " + token)
684                return False
685        return True
686
687    def build_max_sym(self):
688        max_eq = self.xml.get('max_equation')
689        if not max_eq:
690            return "NULL"
691
692        try:
693            val = float(max_eq)
694            if val == 100:
695                if self.xml.get('data_type') == 'uint64':
696                    return "percentage_max_uint64"
697                else:
698                    return "percentage_max_float"
699        except ValueError:
700            pass
701
702        assert self.has_custom_max_func()
703        return "{0}__{1}__{2}__max".format(self.set.gen.chipset,
704                                           self.set.underscore_name,
705                                           self.xml.get('underscore_name'))
706
707
708# Wraps a <set> element from the oa-*.xml files.
709class Set:
710    def __init__(self, gen, xml):
711        self.gen = gen
712        self.xml = xml
713
714        self.counter_vars = {}
715        self.max_funcs = {}
716        self.read_funcs = {}
717
718        xml_counters = self.xml.findall("counter")
719        self.counters = []
720        for xml_counter in xml_counters:
721            counter = Counter(self, xml_counter)
722            self.counters.append(counter)
723            self.counter_vars['$' + counter.get('symbol_name')] = counter
724            self.read_funcs[counter.get('symbol_name')] = counter.read_sym
725            self.max_funcs[counter.get('symbol_name')] = counter.max_sym
726
727        for counter in self.counters:
728            counter.compute_hashes()
729
730    @property
731    def hw_config_guid(self):
732        return self.xml.get('hw_config_guid')
733
734    @property
735    def name(self):
736        return self.xml.get('name')
737
738    @property
739    def symbol_name(self):
740        return self.xml.get('symbol_name')
741
742    @property
743    def underscore_name(self):
744        return self.xml.get('underscore_name')
745
746    def findall(self, path):
747        return self.xml.findall(path)
748
749    def find(self, path):
750        return self.xml.find(path)
751
752
753# Wraps an entire oa-*.xml file.
754class Gen:
755    def __init__(self, filename):
756        self.filename = filename
757        self.xml = et.parse(self.filename)
758        self.chipset = self.xml.find('.//set').get('chipset').lower()
759        self.sets = []
760
761        for xml_set in self.xml.findall(".//set"):
762            self.sets.append(Set(self, xml_set))
763
764
765def main():
766    global c_file
767    global header_file
768
769    parser = argparse.ArgumentParser()
770    parser.add_argument("--header", help="Header file to write", required=True)
771    parser.add_argument("--code", help="C file to write", required=True)
772    parser.add_argument("xml_files", nargs='+', help="List of xml metrics files to process")
773
774    args = parser.parse_args()
775
776    c_file = open(args.code, 'w')
777    header_file = open(args.header, 'w')
778
779    gens = []
780    for xml_file in args.xml_files:
781        gens.append(Gen(xml_file))
782
783
784    copyright = textwrap.dedent("""\
785        /* Autogenerated file, DO NOT EDIT manually! generated by {}
786         *
787         * Copyright (c) 2015 Intel Corporation
788         *
789         * Permission is hereby granted, free of charge, to any person obtaining a
790         * copy of this software and associated documentation files (the "Software"),
791         * to deal in the Software without restriction, including without limitation
792         * the rights to use, copy, modify, merge, publish, distribute, sublicense,
793         * and/or sell copies of the Software, and to permit persons to whom the
794         * Software is furnished to do so, subject to the following conditions:
795         *
796         * The above copyright notice and this permission notice (including the next
797         * paragraph) shall be included in all copies or substantial portions of the
798         * Software.
799         *
800         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
801         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
802         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
803         * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
804         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
805         * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
806         * DEALINGS IN THE SOFTWARE.
807         */
808
809        """).format(os.path.basename(__file__))
810
811    h(copyright)
812    h(textwrap.dedent("""\
813        #pragma once
814
815        struct intel_perf_config;
816
817        """))
818
819    c(copyright)
820    c(textwrap.dedent("""\
821        #include <stdint.h>
822        #include <stdbool.h>
823
824        #include <drm-uapi/i915_drm.h>
825
826        #include "util/hash_table.h"
827        #include "util/ralloc.h"
828
829        """))
830
831    c("#include \"" + os.path.basename(args.header) + "\"")
832
833    c(textwrap.dedent("""\
834        #include "perf/intel_perf.h"
835        #include "perf/intel_perf_setup.h"
836        """))
837
838    names = builtins.set()
839    descs = builtins.set()
840    symbol_names = builtins.set()
841    categories = builtins.set()
842    for gen in gens:
843        for set in gen.sets:
844            for counter in set.counters:
845                names.add(counter.get('name'))
846                symbol_names.add(counter.get('symbol_name'))
847                descs.add(counter.get('description') + " " + desc_units(counter.get('units')))
848                categories.add(counter.get('mdapi_group'))
849
850    name_to_idx = str_to_idx_table(names)
851    output_str_table("name", name_to_idx)
852
853    desc_to_idx = str_to_idx_table(descs)
854    output_str_table("desc", desc_to_idx)
855
856    symbol_name_to_idx = str_to_idx_table(symbol_names)
857    output_str_table("symbol_name", symbol_name_to_idx)
858
859    category_to_idx = str_to_idx_table(categories)
860    output_str_table("category", category_to_idx)
861
862    # Print out all equation functions.
863    for gen in gens:
864        for set in gen.sets:
865            for counter in set.counters:
866                output_counter_read(gen, set, counter)
867                output_counter_max(gen, set, counter)
868
869    c("\n")
870    c("static const struct intel_perf_query_counter_data counters[] = {\n")
871    c_indent(3)
872
873    counter_to_idx = collections.OrderedDict()
874    idx = 0
875    for gen in gens:
876        for set in gen.sets:
877            for counter in set.counters:
878                key = counter_key(counter)
879                if key not in counter_to_idx:
880                    counter_to_idx[key] = idx
881                    output_counter_struct(set, key, idx,
882                                          name_to_idx,
883                                          desc_to_idx,
884                                          symbol_name_to_idx,
885                                          category_to_idx)
886                    idx += 1
887
888    c_outdent(3)
889    c("};\n\n")
890
891    c(textwrap.dedent("""\
892        static void ATTRIBUTE_NOINLINE
893        intel_perf_query_add_counter_uint64(struct intel_perf_query_info *query,
894                                            int counter_idx, size_t offset,
895                                            intel_counter_read_uint64_t oa_counter_max,
896                                            intel_counter_read_uint64_t oa_counter_read)
897        {
898           struct intel_perf_query_counter *dest = &query->counters[query->n_counters++];
899           const struct intel_perf_query_counter_data *counter = &counters[counter_idx];
900
901           dest->name = &name[counter->name_idx];
902           dest->desc = &desc[counter->desc_idx];
903           dest->symbol_name = &symbol_name[counter->symbol_name_idx];
904           dest->category = &category[counter->category_idx];
905
906           dest->offset = offset;
907           dest->type = counter->type;
908           dest->data_type = counter->data_type;
909           dest->units = counter->units;
910           dest->oa_counter_max_uint64 = oa_counter_max;
911           dest->oa_counter_read_uint64 = oa_counter_read;
912        }
913
914        static void ATTRIBUTE_NOINLINE
915        intel_perf_query_add_counter_float(struct intel_perf_query_info *query,
916                                           int counter_idx, size_t offset,
917                                           intel_counter_read_float_t oa_counter_max,
918                                           intel_counter_read_float_t oa_counter_read)
919        {
920           struct intel_perf_query_counter *dest = &query->counters[query->n_counters++];
921           const struct intel_perf_query_counter_data *counter = &counters[counter_idx];
922
923           dest->name = &name[counter->name_idx];
924           dest->desc = &desc[counter->desc_idx];
925           dest->symbol_name = &symbol_name[counter->symbol_name_idx];
926           dest->category = &category[counter->category_idx];
927
928           dest->offset = offset;
929           dest->type = counter->type;
930           dest->data_type = counter->data_type;
931           dest->units = counter->units;
932           dest->oa_counter_max_float = oa_counter_max;
933           dest->oa_counter_read_float = oa_counter_read;
934        }
935
936        static float ATTRIBUTE_NOINLINE
937        percentage_max_float(struct intel_perf_config *perf,
938                             const struct intel_perf_query_info *query,
939                             const struct intel_perf_query_result *results)
940        {
941           return 100;
942        }
943
944        static uint64_t ATTRIBUTE_NOINLINE
945        percentage_max_uint64(struct intel_perf_config *perf,
946                              const struct intel_perf_query_info *query,
947                              const struct intel_perf_query_result *results)
948        {
949           return 100;
950        }
951        """))
952
953    # Print out all metric sets registration functions for each set in each
954    # generation.
955    for gen in gens:
956        for set in gen.sets:
957            counters = set.counters
958
959            c("\n")
960            c("\nstatic void\n")
961            c("{0}_register_{1}_counter_query(struct intel_perf_config *perf)\n".format(gen.chipset, set.underscore_name))
962            c("{\n")
963            c_indent(3)
964
965            c("struct intel_perf_query_info *query = intel_query_alloc(perf, %u);\n" % len(counters))
966            c("\n")
967            c("query->name = \"" + set.name + "\";\n")
968            c("query->symbol_name = \"" + set.symbol_name + "\";\n")
969            c("query->guid = \"" + set.hw_config_guid + "\";\n")
970
971            c("\n")
972            c("struct intel_perf_query_counter *counter = query->counters;\n")
973
974            c("\n")
975            c("/* Note: we're assuming there can't be any variation in the definition ")
976            c(" * of a query between contexts so it's ok to describe a query within a ")
977            c(" * global variable which only needs to be initialized once... */")
978            c("\nif (!query->data_size) {")
979            c_indent(3)
980
981            generate_register_configs(set)
982
983            offset = 0
984            for counter in counters:
985                offset = output_counter_report(set, counter, counter_to_idx, offset)
986
987
988            c("\ncounter = &query->counters[query->n_counters - 1];\n")
989            c("query->data_size = counter->offset + intel_perf_query_counter_get_size(counter);\n")
990
991            c_outdent(3)
992            c("}");
993
994            c("\n_mesa_hash_table_insert(perf->oa_metrics_table, query->guid, query);")
995
996            c_outdent(3)
997            c("}\n")
998
999        h("void intel_oa_register_queries_" + gen.chipset + "(struct intel_perf_config *perf);\n")
1000
1001        c("\nvoid")
1002        c("intel_oa_register_queries_" + gen.chipset + "(struct intel_perf_config *perf)")
1003        c("{")
1004        c_indent(3)
1005
1006        for set in gen.sets:
1007            c("{0}_register_{1}_counter_query(perf);".format(gen.chipset, set.underscore_name))
1008
1009        c_outdent(3)
1010        c("}")
1011
1012
1013if __name__ == '__main__':
1014    main()
1015