#!/usr/bin/env python3
# tools/parsecallstack.py
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.  The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
# License for the specific language governing permissions and limitations
# under the License.
#

import argparse
import os


def parse_args():

    parser = argparse.ArgumentParser(
        """
        parsecallstack.py -c CPUTYPE -f FILENAME\n\
        This file can get the call stack when you get the log with the
        register values from R0 to R15, together with the stack dump.\n
        Then you will get a file with name callstack.cmm, run this file
        in Trace32, load the symbol according to the indication, the call
        stack will pop up.\n
        Trace32 software is available at: https://www.lauterbach.com
        """
    )

    parser.add_argument(
        "-f",
        "--filename",
        action="store",
        help="log file with registers and stack information",
    )
    parser.add_argument(
        "-c",
        "--cputype",
        action="store",
        help='''It supports ARM family CPU such as:
            "CortexM0" "CortexM1"  "CortexM3"  "CortexM4"
            "CortexM7" "CortexM23" "CortexM33" "CortexM35P"
            "CortexR5" "CortexR7"  "CortexA5"  "CortexA7"''',
    )

    return parser.parse_args()


def get_regs(filename):

    reglist = []
    with open(filename, mode="r") as fl:
        for line in fl:
            lst = line.strip("\n").split(" ")
            if "R0:" in lst:
                reglist = lst[-8:]
            if "R8:" in lst:
                reglist += lst[-8:]

    return reglist


def get_stackvalue(filename):

    stackvalue = []
    first = 1
    with open(filename, mode="r") as fl:
        for line in fl:
            lst = line.strip("\n").split(" ")
            if "up_stackdump:" in lst:
                if first == 1:
                    first += 1
                    # strip ":" of sp
                    sp = lst[-9].strip(":")
                    # The first item is the sp to restore the stack.
                    stackvalue.append(sp)
                stackvalue += lst[-8:]

    return stackvalue


def generate_cmm(cpu, regs, stackvalue):

    filename = os.path.join(os.getcwd(), "callstack.cmm")
    with open(filename, mode="w") as fl:
        # Select the CPU and symbol.
        fl.write("SYStem.CPU %d\n" % cpu)
        fl.write("SYS.M UP\n")
        fl.write("Data.LOAD *\n")
        fl.write("\n")

        # Set R0-R15.
        for num in range(len(regs)):
            fl.write("Register.Set R%d 0x%s\n" % num, regs[num])
        fl.write("\n")

        # Recover the value in stack.
        sp = int(stackvalue[0], 16)
        for num in range(len(stackvalue) - 1):
            address = hex(sp + num * 4)
            value = stackvalue[num + 1]
            fl.write("Data.Set ZSD:%d %%LE %%Long 0x%d\n" % address, value)
        fl.write("\n")

        # Show the call stack.
        fl.write("data.view %%sYmbol.long %x\n" % sp)
        fl.write("frame.view /Locals /Caller\n")


if __name__ == "__main__":
    args = parse_args()
    regs = get_regs(args.filename)
    stackvalue = get_stackvalue(args.filename)
    generate_cmm(args.cpu, regs, stackvalue)