# SPDX-License-Identifier: GPL-2.0-or-later

"""
Generate .stp file that printfs log messages (DTrace with SystemTAP only).
"""

__author__     = "Daniel P. Berrange <berrange@redhat.com>"
__copyright__  = "Copyright (C) 2014-2019, Red Hat, Inc."
__license__    = "GPL version 2 or (at your option) any later version"

__maintainer__ = "Daniel Berrange"
__email__      = "berrange@redhat.com"

import re

from tracetool import out
from tracetool.backend.dtrace import binary, probeprefix
from tracetool.backend.simple import is_string
from tracetool.format.stap import stap_escape


STATE_SKIP = 0
STATE_LITERAL = 1
STATE_MACRO = 2

def c_macro_to_format(macro):
    if macro.startswith("PRI"):
        return macro[3]

    raise Exception("Unhandled macro '%s'" % macro)

def c_fmt_to_stap(fmt):
    state = 0
    bits = []
    literal = ""
    macro = ""
    escape = 0;
    for i in range(len(fmt)):
        if fmt[i] == '\\':
            if escape:
                escape = 0
            else:
                escape = 1
            if state != STATE_LITERAL:
                raise Exception("Unexpected escape outside string literal")
            literal = literal + fmt[i]
        elif fmt[i] == '"' and not escape:
            if state == STATE_LITERAL:
                state = STATE_SKIP
                bits.append(literal)
                literal = ""
            else:
                if state == STATE_MACRO:
                    bits.append(c_macro_to_format(macro))
                    macro = ""
                state = STATE_LITERAL
        elif fmt[i] == ' ' or fmt[i] == '\t':
            if state == STATE_MACRO:
                bits.append(c_macro_to_format(macro))
                macro = ""
                state = STATE_SKIP
            elif state == STATE_LITERAL:
                literal = literal + fmt[i]
        else:
            escape = 0
            if state == STATE_SKIP:
                state = STATE_MACRO

            if state == STATE_LITERAL:
                literal = literal + fmt[i]
            else:
                macro = macro + fmt[i]

    if state == STATE_MACRO:
        bits.append(c_macro_to_format(macro))
    elif state == STATE_LITERAL:
        bits.append(literal)

    # All variables in systemtap are 64-bit in size
    # The "%l" integer size qualifier is thus redundant
    # and "%ll" is not valid at all. Similarly the size_t
    # based "%z" size qualifier is not valid. We just
    # strip all size qualifiers for sanity.
    fmt = re.sub(r"%(\d*)(l+|z)(x|u|d)", r"%\1\3", "".join(bits))
    return fmt

def generate(events, backend, group):
    out('/* This file is autogenerated by tracetool, do not edit. */',
        '/* SPDX-License-Identifier: GPL-2.0-or-later */',
        '')

    for event_id, e in enumerate(events):
        if 'disable' in e.properties:
            continue

        out('probe %(probeprefix)s.log.%(name)s = %(probeprefix)s.%(name)s ?',
            '{',
            probeprefix=probeprefix(),
            name=e.name)

        # Get references to userspace strings
        for type_, name in e.args:
            name = stap_escape(name)
            if is_string(type_):
                out('    try {',
                    '        arg%(name)s_str = %(name)s ? ' +
                    'user_string_n(%(name)s, 512) : "<null>"',
                    '    } catch {}',
                    name=name)

        # Determine systemtap's view of variable names
        fields = ["pid()", "gettimeofday_ns()"]
        for type_, name in e.args:
            name = stap_escape(name)
            if is_string(type_):
                fields.append("arg" + name + "_str")
            else:
                fields.append(name)

        # Emit the entire record in a single SystemTap printf()
        arg_str = ', '.join(arg for arg in fields)
        fmt_str = "%d@%d " + e.name + " " + c_fmt_to_stap(e.fmt) + "\\n"
        out('    printf("%(fmt_str)s", %(arg_str)s)',
            fmt_str=fmt_str, arg_str=arg_str)

        out('}')

    out()
