From 657a8c8f1e2c56c3e4f41cb251c4330477f2bb76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E6=99=93=E4=BC=9F?= <39303645+Shixiaowei02@users.noreply.github.com> Date: Tue, 14 Sep 2021 10:35:37 +0800 Subject: [PATCH] experimental feature: error status, test=develop (#35624) --- paddle/fluid/inference/CMakeLists.txt | 1 + .../inference/api/paddle_infer_contrib.cc | 39 +++++++++ .../inference/api/paddle_infer_contrib.h | 86 +++++++++++++++++++ .../fluid/inference/tests/api/CMakeLists.txt | 2 + .../api/paddle_infer_api_errors_tester.cc | 71 +++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 paddle/fluid/inference/tests/api/paddle_infer_api_errors_tester.cc diff --git a/paddle/fluid/inference/CMakeLists.txt b/paddle/fluid/inference/CMakeLists.txt index e0cda77d4fe..13dc22c4dff 100644 --- a/paddle/fluid/inference/CMakeLists.txt +++ b/paddle/fluid/inference/CMakeLists.txt @@ -76,6 +76,7 @@ set(SHARED_INFERENCE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/api/api.cc ${CMAKE_CURRENT_SOURCE_DIR}/api/api_impl.cc ${CMAKE_CURRENT_SOURCE_DIR}/api/analysis_predictor.cc + ${CMAKE_CURRENT_SOURCE_DIR}/api/paddle_infer_contrib.cc ${CMAKE_CURRENT_SOURCE_DIR}/api/details/zero_copy_tensor.cc ${CMAKE_CURRENT_SOURCE_DIR}/utils/io_utils.cc ${PADDLE_CUSTOM_OP_SRCS}) diff --git a/paddle/fluid/inference/api/paddle_infer_contrib.cc b/paddle/fluid/inference/api/paddle_infer_contrib.cc index aad1c3fa6f8..57b5167337e 100644 --- a/paddle/fluid/inference/api/paddle_infer_contrib.cc +++ b/paddle/fluid/inference/api/paddle_infer_contrib.cc @@ -186,5 +186,44 @@ void TensorUtils::CopyTensorAsync(Tensor* p_dst, const Tensor& src, CopyTensorImpl(p_dst, src, nullptr, cb, cb_params); } +struct Status::Impl { + int ec{0}; + std::string msg; +}; + +Status::Status() noexcept : impl_(new Impl) {} +Status::Status(const Status& status) noexcept : impl_(new Impl) { + *impl_ = *status.impl_; +} + +Status& Status::operator=(const Status& status) noexcept { + *impl_ = *status.impl_; + return *this; +} +Status::Status(std::exception_ptr e) noexcept : impl_(new Impl) { + constexpr int kDefaultError{-1}; + impl_->ec = kDefaultError; + try { + std::rethrow_exception(e); + } catch (paddle::platform::EnforceNotMet& e) { + // Add one to the error code to make the number zero a non-error + // status code. + impl_->ec = e.code() + 1; + impl_->msg = e.what(); + } catch (const std::exception& e) { + impl_->msg = e.what(); + } +} +Status Status::OK() noexcept { return Status(); } +bool Status::ok() const noexcept { return impl_->ec == 0; } +Status::Code Status::code() const noexcept { return impl_->ec; } +const std::string& Status::error_message() const noexcept { return impl_->msg; } +bool Status::operator==(const Status& x) const noexcept { + return code() == x.code() && error_message() == x.error_message(); +} +bool Status::operator!=(const Status& x) const noexcept { + return !(*this == x); +} + } // namespace contrib } // namespace paddle_infer diff --git a/paddle/fluid/inference/api/paddle_infer_contrib.h b/paddle/fluid/inference/api/paddle_infer_contrib.h index 7d35567e430..561b83683ec 100644 --- a/paddle/fluid/inference/api/paddle_infer_contrib.h +++ b/paddle/fluid/inference/api/paddle_infer_contrib.h @@ -36,5 +36,91 @@ class TensorUtils { void* cb_params); }; +/// \brief A status class, used to intercept exceptions and convert +/// them into a status number. +class Status { + public: + using Code = int; + struct Impl; + + Status() noexcept; + explicit Status(std::exception_ptr e) noexcept; + + Status(const Status&) noexcept; + Status& operator=(const Status&) noexcept; + Status& operator=(Status&&) = default; + Status(Status&&) = default; + + /// + /// \brief Construct a status which indicate ok. + /// + /// \return A status which indicate ok. + /// + static Status OK() noexcept; + + /// + /// \brief Determine whether the status is ok. + /// + /// \return Whether the status is ok. + /// + bool ok() const noexcept; + + /// + /// \brief Return the error code. + /// The meaning corresponds to the following. + /// + /// CODE IMPLICATION + /// -1 UNKNOWN + /// 0 NORMAL + /// 1 LEGACY + /// 2 INVALID_ARGUMENT + /// 3 NOT_FOUND + /// 4 OUT_OF_RANGE + /// 5 ALREADY_EXISTS + /// 6 RESOURCE_EXHAUSTED + /// 7 PRECONDITION_NOT_MET + /// 8 PERMISSION_DENIED + /// 9 EXECUTION_TIMEOUT + /// 10 UNIMPLEMENTED + /// 11 UNAVAILABLE + /// 12 FATAL + /// 13 EXTERNAL + /// + /// \return The error code. + /// + Code code() const noexcept; + + /// + /// \brief Return the error message. + /// + /// \return The error message. + /// + const std::string& error_message() const noexcept; + + bool operator==(const Status& x) const noexcept; + bool operator!=(const Status& x) const noexcept; + + private: + std::shared_ptr impl_; +}; + +/// +/// \brief A wrapper used to provide exception safety. +/// +/// \param func Wrapped function. +/// \param args Parameters of the wrapped function. +/// \return State result of calling function. +/// +template +Status get_status(Func func, Args&&... args) noexcept( + noexcept(Status(std::declval()))) { + try { + func(std::forward(args)...); + } catch (...) { + return Status(std::current_exception()); + } + return Status::OK(); +} + } // namespace contrib } // namespace paddle_infer diff --git a/paddle/fluid/inference/tests/api/CMakeLists.txt b/paddle/fluid/inference/tests/api/CMakeLists.txt index 05bc2607851..11187a1c79f 100644 --- a/paddle/fluid/inference/tests/api/CMakeLists.txt +++ b/paddle/fluid/inference/tests/api/CMakeLists.txt @@ -708,6 +708,8 @@ if(WITH_GPU) set_tests_properties(paddle_infer_api_copy_tensor_tester PROPERTIES TIMEOUT 30) endif() +cc_test(paddle_infer_api_errors_test SRCS paddle_infer_api_errors_tester.cc DEPS paddle_inference_api) + if("$ENV{CI_SKIP_CPP_TEST}" STREQUAL "ON") return() endif() diff --git a/paddle/fluid/inference/tests/api/paddle_infer_api_errors_tester.cc b/paddle/fluid/inference/tests/api/paddle_infer_api_errors_tester.cc new file mode 100644 index 00000000000..c5a0746c4d7 --- /dev/null +++ b/paddle/fluid/inference/tests/api/paddle_infer_api_errors_tester.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved. +// +// Licensed 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. + +#include "gflags/gflags.h" +#include "glog/logging.h" +#include "gtest/gtest.h" + +#include "paddle/fluid/inference/api/paddle_infer_contrib.h" +#include "paddle/fluid/platform/enforce.h" + +namespace paddle_infer { +namespace contrib { + +TEST(Status, ctor) { CHECK(Status::OK().ok()); } + +struct FakeException { + void pd_exception(int a) const { + PADDLE_ENFORCE_NE(a, a, + paddle::platform::errors::InvalidArgument( + "This is a preset error message used to verify " + "whether the exception meets expectations: %d, %d.", + a, a)); + } + [[noreturn]] void base_exception() const { throw std::exception(); } + void no_exception() const noexcept {} +}; + +TEST(Status, pd_exception) { + FakeException e; + Status status = get_status([&]() { e.pd_exception(1); }); + CHECK(!status.ok()); + CHECK(status == status); + CHECK(!(status != status)); + CHECK_EQ(status.code(), paddle::platform::error::INVALID_ARGUMENT + 1); + LOG(INFO) << status.error_message(); +} + +TEST(Status, basic_exception) { + FakeException e; + Status status; + status = get_status([&]() { e.base_exception(); }); + CHECK(!status.ok()); + LOG(INFO) << status.error_message(); +} + +TEST(Status, no_exception) { + FakeException e; + Status status; + status = get_status([&]() { e.no_exception(); }); + CHECK(status.ok()); +} + +TEST(Status, copy) { + Status status; + Status status_1(status); + status_1 = status; +} + +} // namespace contrib +} // namespace paddle_infer -- GitLab