/**
 * \file src/core/impl/exception.cpp
 * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
 *
 * Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

#include "megbrain/exception.h"
#include "megbrain/common.h"
#include "megbrain/utils/debug.h"
#include "megbrain/comp_node_env.h"

using namespace mgb;

namespace {
    class MegDNNErrorHandler final: public megdnn::ErrorHandler {
        static MegDNNErrorHandler inst;
        void do_on_megdnn_error(const std::string &msg) override {
            mgb_throw_raw(MegDNNError{
                    LogLevel::NO_LOG == get_log_level() ? "" : msg});
        }

        void do_on_tensor_reshape_error(const std::string &msg) override {
            mgb_throw_raw(TensorReshapeError{
                    LogLevel::NO_LOG == get_log_level() ? "" : msg});
        }

        public:
            MegDNNErrorHandler() {
                set_handler(this);
            }
    };
    MegDNNErrorHandler MegDNNErrorHandler::inst;
}

void MegBrainError::init()
{
    m_msg.append("\n");
#if MGB_ENABLE_DEBUG_UTIL
    debug::backtrace(2).fmt_to_str(m_msg);
    static bool print_exc = MGB_GETENV("MGB_PRINT_EXC");
    if (print_exc) {
        fprintf(stderr, "mgb: exception occurred: %s\n", m_msg.c_str());
    }
#endif
}

CudaError::CudaError(const std::string &msg):
    SystemError(msg)
{
    m_msg.append(get_cuda_extra_info());
}

std::string CudaError::get_cuda_extra_info() {
#if MGB_CUDA
    // get last error and clear error
    auto err = cudaGetLastError();
    int dev = -1;
    cudaGetDevice(&dev);
    size_t free_byte = 0, total_byte = 0;
    cudaMemGetInfo(&free_byte, &total_byte);
    constexpr double SIZE2MB = 1.0 / 1024 / 1024;
    return ssprintf("(last_err=%d(%s) "
            "device=%d mem_free=%.3fMiB mem_tot=%.3fMiB)",
            err, cudaGetErrorString(err),
            dev, free_byte * SIZE2MB, total_byte * SIZE2MB);
#else
    return "cuda disabled at compile time";
#endif
}


AtlasError::AtlasError(const std::string &msg):
    SystemError(msg)
{
}



ROCmError::ROCmError(const std::string &msg):
    SystemError(msg)
{
    m_msg.append(get_rocm_extra_info());
}

std::string ROCmError::get_rocm_extra_info() {
#if MGB_ROCM
    // get last error and clear error
    auto err = hipGetLastError();
    int dev = -1;
    hipGetDevice(&dev);
    size_t free_byte = 0, total_byte = 0;
    hipMemGetInfo(&free_byte, &total_byte);
    constexpr double SIZE2MB = 1.0 / 1024 / 1024;
    return ssprintf("(last_err=%d(%s) "
            "device=%d mem_free=%.3fMiB mem_tot=%.3fMiB)",
            err, hipGetErrorString(err),
            dev, free_byte * SIZE2MB, total_byte * SIZE2MB);
#else
    return "rocm disabled at compile time";
#endif
}

CnrtError::CnrtError(const std::string& msg) : SystemError(msg) {
    m_msg.append(get_cnrt_extra_info());
}

std::string CnrtError::get_cnrt_extra_info() {
#if MGB_CAMBRICON
    // get last error
    auto err = cnrtGetLastErr();
    return ssprintf("(last_err=%d(%s))", err, cnrtGetErrorStr(err));
#else
    return "cnrt disabled at compile time";
#endif
}

CndevError::CndevError(const std::string& msg) : SystemError(msg) {}

CnmlError::CnmlError(const std::string& msg) : SystemError(msg) {}

bool mgb::has_uncaught_exception() {
#if MGB_ENABLE_EXCEPTION
#if __cplusplus > 201402L
    // C++17; see https://stackoverflow.com/questions/38456127/what-is-the-value-of-cplusplus-for-c17
    return std::uncaught_exceptions() != 0;
#else
    return std::uncaught_exception();
#endif
#else
    return false;
#endif
}

// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}

