LCOV - code coverage report
Current view: top level - src/backend/jit/llvm - llvmjit_error.cpp (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 22 37 59.5 %
Date: 2023-12-07 07:10:44 Functions: 5 10 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * llvmjit_error.cpp
       4             :  *    LLVM error related handling that requires interfacing with C++
       5             :  *
       6             :  * Unfortunately neither (re)setting the C++ new handler, nor the LLVM OOM
       7             :  * handler are exposed to C. Therefore this file wraps the necessary code.
       8             :  *
       9             :  * Copyright (c) 2016-2023, PostgreSQL Global Development Group
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/backend/jit/llvm/llvmjit_error.cpp
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : 
      17             : extern "C"
      18             : {
      19             : #include "postgres.h"
      20             : }
      21             : 
      22             : #include <llvm/Support/ErrorHandling.h>
      23             : 
      24             : #include "jit/llvmjit.h"
      25             : 
      26             : #include <new>
      27             : 
      28             : static int fatal_new_handler_depth = 0;
      29             : static std::new_handler old_new_handler = NULL;
      30             : 
      31             : static void fatal_system_new_handler(void);
      32             : #if LLVM_VERSION_MAJOR > 4
      33             : static void fatal_llvm_new_handler(void *user_data, const char *reason, bool gen_crash_diag);
      34             : #if LLVM_VERSION_MAJOR < 14
      35             : static void fatal_llvm_new_handler(void *user_data, const std::string& reason, bool gen_crash_diag);
      36             : #endif
      37             : #endif
      38             : static void fatal_llvm_error_handler(void *user_data, const char *reason, bool gen_crash_diag);
      39             : #if LLVM_VERSION_MAJOR < 14
      40             : static void fatal_llvm_error_handler(void *user_data, const std::string& reason, bool gen_crash_diag);
      41             : #endif
      42             : 
      43             : 
      44             : /*
      45             :  * Enter a section in which C++ and LLVM errors are treated as FATAL errors.
      46             :  *
      47             :  * This is necessary for LLVM as LLVM's error handling for such cases
      48             :  * (exit()ing, throwing std::bad_alloc() if compiled with exceptions, abort())
      49             :  * isn't compatible with postgres error handling.  Thus in sections where LLVM
      50             :  * code, not LLVM generated functions!, is executing, standard new, LLVM OOM
      51             :  * and LLVM fatal errors (some OOM errors masquerade as those) are redirected
      52             :  * to our own error handlers.
      53             :  *
      54             :  * These error handlers use FATAL, because there's no reliable way from within
      55             :  * LLVM to throw an error that's guaranteed not to corrupt LLVM's state.
      56             :  *
      57             :  * To avoid disturbing extensions using C++ and/or LLVM, these handlers are
      58             :  * unset when not executing LLVM code. There is no need to call
      59             :  * llvm_leave_fatal_on_oom() when ERRORing out, error recovery resets the
      60             :  * handlers in that case.
      61             :  */
      62             : void
      63       17836 : llvm_enter_fatal_on_oom(void)
      64             : {
      65       17836 :     if (fatal_new_handler_depth == 0)
      66             :     {
      67       17836 :         old_new_handler = std::set_new_handler(fatal_system_new_handler);
      68             : #if LLVM_VERSION_MAJOR > 4
      69       17836 :         llvm::install_bad_alloc_error_handler(fatal_llvm_new_handler);
      70             : #endif
      71       17836 :         llvm::install_fatal_error_handler(fatal_llvm_error_handler);
      72             :     }
      73       17836 :     fatal_new_handler_depth++;
      74       17836 : }
      75             : 
      76             : /*
      77             :  * Leave fatal error section started with llvm_enter_fatal_on_oom().
      78             :  */
      79             : void
      80       17836 : llvm_leave_fatal_on_oom(void)
      81             : {
      82       17836 :     fatal_new_handler_depth--;
      83       17836 :     if (fatal_new_handler_depth == 0)
      84             :     {
      85       17836 :         std::set_new_handler(old_new_handler);
      86             : #if LLVM_VERSION_MAJOR > 4
      87       17836 :         llvm::remove_bad_alloc_error_handler();
      88             : #endif
      89       17836 :         llvm::remove_fatal_error_handler();
      90             :     }
      91       17836 : }
      92             : 
      93             : /*
      94             :  * Are we currently in a fatal-on-oom section? Useful to skip cleanup in case
      95             :  * of errors.
      96             :  */
      97             : bool
      98         770 : llvm_in_fatal_on_oom(void)
      99             : {
     100         770 :     return fatal_new_handler_depth > 0;
     101             : }
     102             : 
     103             : /*
     104             :  * Reset fatal error handling. This should only be called in error recovery
     105             :  * loops like PostgresMain()'s.
     106             :  */
     107             : void
     108        3156 : llvm_reset_after_error(void)
     109             : {
     110        3156 :     if (fatal_new_handler_depth != 0)
     111             :     {
     112           0 :         std::set_new_handler(old_new_handler);
     113             : #if LLVM_VERSION_MAJOR > 4
     114           0 :         llvm::remove_bad_alloc_error_handler();
     115             : #endif
     116           0 :         llvm::remove_fatal_error_handler();
     117             :     }
     118        3156 :     fatal_new_handler_depth = 0;
     119        3156 : }
     120             : 
     121             : void
     122       23868 : llvm_assert_in_fatal_section(void)
     123             : {
     124             :     Assert(fatal_new_handler_depth > 0);
     125       23868 : }
     126             : 
     127             : static void
     128           0 : fatal_system_new_handler(void)
     129             : {
     130           0 :     ereport(FATAL,
     131             :             (errcode(ERRCODE_OUT_OF_MEMORY),
     132             :              errmsg("out of memory"),
     133             :              errdetail("while in LLVM")));
     134             : }
     135             : 
     136             : #if LLVM_VERSION_MAJOR > 4
     137             : static void
     138           0 : fatal_llvm_new_handler(void *user_data,
     139             :                        const char *reason,
     140             :                        bool gen_crash_diag)
     141             : {
     142           0 :     ereport(FATAL,
     143             :             (errcode(ERRCODE_OUT_OF_MEMORY),
     144             :              errmsg("out of memory"),
     145             :              errdetail("While in LLVM: %s", reason)));
     146             : }
     147             : #if LLVM_VERSION_MAJOR < 14
     148             : static void
     149           0 : fatal_llvm_new_handler(void *user_data,
     150             :                        const std::string& reason,
     151             :                        bool gen_crash_diag)
     152             : {
     153           0 :     fatal_llvm_new_handler(user_data, reason.c_str(), gen_crash_diag);
     154           0 : }
     155             : #endif
     156             : #endif
     157             : 
     158             : static void
     159           0 : fatal_llvm_error_handler(void *user_data,
     160             :                          const char *reason,
     161             :                          bool gen_crash_diag)
     162             : {
     163           0 :     ereport(FATAL,
     164             :             (errcode(ERRCODE_OUT_OF_MEMORY),
     165             :              errmsg("fatal llvm error: %s", reason)));
     166             : }
     167             : 
     168             : #if LLVM_VERSION_MAJOR < 14
     169             : static void
     170           0 : fatal_llvm_error_handler(void *user_data,
     171             :                          const std::string& reason,
     172             :                          bool gen_crash_diag)
     173             : {
     174           0 :     fatal_llvm_error_handler(user_data, reason.c_str(), gen_crash_diag);
     175           0 : }
     176             : #endif

Generated by: LCOV version 1.14