def get_memory_size(fn_log):
    memory_size_regex = re.compile("Memory Size: \d*")
    try:
        if memory_size := re.search(memory_size_regex, fn_log):
            return int(memory_size.group().replace("Memory Size: ", "").strip())
    except TypeError:
        log("Could not parse Memory Size from lambda invocation log", ERROR)
def parse_logs(test_num):
    log(f'Parsing logs for test: {test_num}')
    test_logs_path = get_logs_root_path(test_num)
    logs_dir = f'{logs_dir_path}/{test_logs_path}/'

    for log_dir in os.listdir(logs_dir):
        fn_name = log_dir
        fn_logs_dir = logs_dir + fn_name

        image_size_mb = get_fn_image_size(fn_name)
        csv_results = []
        for log_file in os.listdir(fn_logs_dir):
            log(f'Parsing logs file {log_file} in dir {fn_logs_dir}')
            fn_log = read_file(fn_logs_dir + '/' + log_file)

            csv_results.append([
                image_size_mb,
                get_memory_size(fn_log),
                get_init_duration(fn_log),
                get_artificial_init_duration(fn_log),
                get_duration(fn_log),
                get_available_threads(fn_log)
            ])

        full_path = results_dir_path + test_logs_path + fn_name + '.csv'
        write_to_csv(csv_results, results_header, full_path)

    write_average_init_duration(test_num, results_dir_path + test_logs_path)
def get_init_duration(fn_log):
    init_duration_regex = re.compile("Init Duration: \d*.\d*")
    try:
        if init_duration := re.search(init_duration_regex, fn_log):
            return float(init_duration.group().replace("Init Duration: ", "").strip())
    except TypeError:
        log("Could not parse Init Duration from lambda invocation log", ERROR)

    return 0
def get_available_threads(fn_log):
    available_threads_regex = re.compile("Available threads: \d*.\d*")
    try:
        if threads := re.search(available_threads_regex, fn_log):
            return int(threads.group().replace("Available threads: ", "").strip())
    except TypeError:
        log("Could not parse Available threads from lambda invocation log", ERROR)

    return 1
def get_artificial_init_duration(fn_log):
    billed_duration_regex = re.compile("Artificial initialization duration took: \d*.\d*")
    try:
        if billed_duration := re.search(billed_duration_regex, fn_log):
            return float(billed_duration.group().replace("Artificial initialization duration took: ", "").strip())
    except TypeError:
        log("Could not parse Artificial Init Duration from lambda invocation log", ERROR)

    return 0
def is_cold_started(log_str):
    is_cold_start_log_regex = re.compile("Invoked a cold start function")
    try:
        if re.search(is_cold_start_log_regex, log_str):
            return True
        else:
            log("Function was not started cold. Skipping writing log", DEBUG)
            return False
    except TypeError:
        return False
def parse_response_logs(fn_name, invocation_response):
    log("Parsing function {} invocation response logs".format(fn_name))
    log_result_regex = re.compile("\"LogResult\": [^,]*")
    base64_log = ""
    try:
        if log_result := re.search(log_result_regex, invocation_response):
            base64_log = log_result.group().replace("\"LogResult\": ",
                                                    "").replace("\"", "")
    except TypeError:
        log("Could not parse invocation log")
        return base64_log

    return decode_base64(base64_log)
def write_response_logs(fn_name, invocation_response, test_num):
    log_str = parse_response_logs(fn_name, invocation_response)
    if not is_cold_started(log_str):
        return

    test_num_logs_path = get_logs_root_path(test_num)
    uuid_regex = re.compile(
        "[0-9a-f]{8}\\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\\b[0-9a-f]{12}")
    try:
        if uuid := re.search(uuid_regex, fn_name):
            fn_id = uuid.group()
            now = datetime.now().strftime("%Y-%m-%d-%H:%M:%S")
            log_dir = f'{logs_dir_path}/{test_num_logs_path}/{fn_name.replace(fn_id, "")}/{now}-{fn_id}'
            write_to_file(log_str, log_dir)
    except TypeError:
        log("Could not parse request id from invocation logs", ERROR)
def get_fn_image_size(fn_name):
    fn_tag = get_fn_tag(fn_name)
    image_details = run_executable(
        executable_path=describe_image_exec_path,
        args=[REPOSITORY_NAME, fn_tag]
    )

    image_byte_size_regex = re.compile("\"imageSizeInBytes\": \\d*")
    try:
        if image_byte_size := re.search(image_byte_size_regex, image_details):
            image_bytes = int(image_byte_size.group().replace("\"imageSizeInBytes\": ", ""))
            return round(image_bytes / 1000 / 1000, 2)
    except TypeError:
        log("Could not get function image size. This might be because provided image name is wrong", ERROR)

    return 0