diff options
Diffstat (limited to 'indra/llcommon')
| -rw-r--r-- | indra/llcommon/always_return.h | 16 | ||||
| -rw-r--r-- | indra/llcommon/llcoros.cpp | 55 | ||||
| -rw-r--r-- | indra/llcommon/llerror.h | 11 | ||||
| -rw-r--r-- | indra/llcommon/llexception.cpp | 44 | ||||
| -rw-r--r-- | indra/llcommon/llexception.h | 114 | ||||
| -rw-r--r-- | indra/llcommon/llprocessor.cpp | 73 | ||||
| -rw-r--r-- | indra/llcommon/llsdutil.cpp | 4 | ||||
| -rw-r--r-- | indra/llcommon/llsys.cpp | 71 | ||||
| -rw-r--r-- | indra/llcommon/tests/llleap_test.cpp | 2 | ||||
| -rw-r--r-- | indra/llcommon/tests/llstring_test.cpp | 4 | 
10 files changed, 259 insertions, 135 deletions
diff --git a/indra/llcommon/always_return.h b/indra/llcommon/always_return.h index a206471da5..b99eb49096 100644 --- a/indra/llcommon/always_return.h +++ b/indra/llcommon/always_return.h @@ -79,6 +79,22 @@ namespace LL          DESIRED mDefault;      }; +    // specialize for AlwaysReturn<void> +    template <> +    struct AlwaysReturn<void> +    { +    public: +        AlwaysReturn() {} + +        // callable returns a type not convertible to DESIRED, return default +        template <typename CALLABLE, typename... ARGS> +        void operator()(CALLABLE&& callable, ARGS&&... args) +        { +            // discard whatever callable(args) returns +            std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...); +        } +    }; +      /**       * always_return<T>(some_function, some_args...) calls       * some_function(some_args...). It is guaranteed to return a value of type diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 8612f9353f..6285ac86ad 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -123,7 +123,7 @@ LLCoros::LLCoros():      // Previously we used      // boost::context::guarded_stack_allocator::default_stacksize();      // empirically this is insufficient. -    mStackSize(1024*1024), +    mStackSize(512*1024),      // mCurrent does NOT own the current CoroData instance -- it simply      // points to it. So initialize it with a no-op deleter.      mCurrent{ [](CoroData*){} } @@ -155,7 +155,7 @@ void LLCoros::cleanupSingleton()          // don't use llcoro::suspend() because that module depends          // on this one          // This will yield current(main) thread and will let active -        // corutines run once +        // coroutines run once          boost::this_fiber::yield();      }      printActiveCoroutines("after pumping"); @@ -286,55 +286,6 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl      return name;  } -namespace -{ - -#if LL_WINDOWS - -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific - -U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) -{ -    if (code == STATUS_MSC_EXCEPTION) -    { -        // C++ exception, go on -        return EXCEPTION_CONTINUE_SEARCH; -    } -    else -    { -        // handle it -        return EXCEPTION_EXECUTE_HANDLER; -    } -} - -void sehandle(const LLCoros::callable_t& callable) -{ -    __try -    { -        callable(); -    } -    __except (exception_filter(GetExceptionCode(), GetExceptionInformation())) -    { -        // convert to C++ styled exception -        // Note: it might be better to use _se_set_translator -        // if you want exception to inherit full callstack -        char integer_string[512]; -        sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); -        throw std::exception(integer_string); -    } -} - -#else  // ! LL_WINDOWS - -inline void sehandle(const LLCoros::callable_t& callable) -{ -    callable(); -} - -#endif // ! LL_WINDOWS - -} // anonymous namespace -  // Top-level wrapper around caller's coroutine callable.  // Normally we like to pass strings and such by const reference -- but in this  // case, we WANT to copy both the name and the callable to our local stack! @@ -348,7 +299,7 @@ void LLCoros::toplevel(std::string name, callable_t callable)      // run the code the caller actually wants in the coroutine      try      { -        sehandle(callable); +        LL::seh::catcher(callable);      }      catch (const Stop& exc)      { diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 6176ce0d1d..7353a36c5f 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -95,6 +95,11 @@ const int LL_ERR_NOERR = 0;  #define LL_STATIC_ASSERT(func, msg) static_assert(func, msg)  #define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) static_assert(false, msg)  #else +#if LL_LINUX +// We need access to raise and SIGSEGV +#include <signal.h> +#endif +  #define LL_STATIC_ASSERT(func, msg) BOOST_STATIC_ASSERT(func)  #define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) BOOST_STATIC_ASSERT(sizeof(type) != 0 && false);  #endif @@ -408,10 +413,16 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;  #define LL_NEWLINE '\n'  // Use this only in LL_ERRS or in a place that LL_ERRS may not be used + +#ifndef LL_LINUX  #define LLERROR_CRASH                                   \  {                                                       \      crashdriver([](int* ptr){ *ptr = 0; exit(*ptr); }); \  } +#else +// For Linux we just call raise and be done with it. No fighting the compiler to create a crashing code snippet. +#define LLERROR_CRASH raise(SIGSEGV ); +#endif  #define LL_ENDL                                         \              LLError::End();                             \ diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index c0154a569f..107fdc2b2d 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -15,7 +15,12 @@  #include "llexception.h"  // STL headers  // std headers +#include <iomanip> +#include <sstream>  #include <typeinfo> +#if LL_WINDOWS +#include <excpt.h> +#endif // LL_WINDOWS  // external library headers  #include <boost/exception/diagnostic_information.hpp>  #include <boost/exception/error_info.hpp> @@ -29,7 +34,6 @@  // On Windows, header-only implementation causes macro collisions -- use  // prebuilt library  #define BOOST_STACKTRACE_LINK -#include <excpt.h>  #endif // LL_WINDOWS  #include <boost/stacktrace.hpp> @@ -94,25 +98,47 @@ void annotate_exception_(boost::exception& exc)  // For windows SEH exception handling we sometimes need a filter that will  // separate C++ exceptions from C SEH exceptions -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific +static constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific +static constexpr U32 STATUS_STACK_FULL    = 0xC00000FD; -U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) +void LL::seh::fill_stacktrace(std::string& stacktrace, U32 code)  { -    const auto stack = to_string(boost::stacktrace::stacktrace()); -    LL_WARNS() << "SEH Exception handled (that probably shouldn't be): Code " << code -        << "\n Stack trace: \n" -        << stack << LL_ENDL; +    // Sadly, despite its diagnostic importance, trying to capture a +    // stacktrace when the stack is already blown only terminates us faster. +    if (code == STATUS_STACK_FULL) +    { +        stacktrace = "(stack overflow, no traceback)"; +    } +    else +    { +        stacktrace = to_string(boost::stacktrace::stacktrace()); +    } +} +U32 LL::seh::common_filter(U32 code, struct _EXCEPTION_POINTERS*) +{      if (code == STATUS_MSC_EXCEPTION)      { -        // C++ exception, go on +        // C++ exception, don't stop at this handler          return EXCEPTION_CONTINUE_SEARCH;      }      else      { -        // handle it +        // This is a non-C++ exception, e.g. hardware check. +        // Pass control into the handler block.          return EXCEPTION_EXECUTE_HANDLER;      }  } +void LL::seh::rethrow(U32 code, const std::string& stacktrace) +{ +    std::ostringstream out; +    out << "Windows exception 0x" << std::hex << code; +    if (! stacktrace.empty()) +    { +        out << '\n' << stacktrace; +    } +    LLTHROW(Windows_SEH_exception(out.str())); +} +  #endif //LL_WINDOWS diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 68e609444e..f58a553eb3 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -12,6 +12,7 @@  #if ! defined(LL_LLEXCEPTION_H)  #define LL_LLEXCEPTION_H +#include "always_return.h"  #include <stdexcept>  #include <boost/exception/exception.hpp>  #include <boost/throw_exception.hpp> @@ -102,14 +103,115 @@ void crash_on_unhandled_exception_(const char*, int, const char*, const std::str       log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT)  void log_unhandled_exception_(const char*, int, const char*, const std::string&); +/***************************************************************************** +*   Structured Exception Handling +*****************************************************************************/ +// this is used in platform-generic code -- define outside #if LL_WINDOWS +struct Windows_SEH_exception: public LLException +{ +    Windows_SEH_exception(const std::string& what): LLException(what) {} +}; + +namespace LL +{ +namespace seh +{ + +#if LL_WINDOWS //------------------------------------------------------------- + +void fill_stacktrace(std::string& stacktrace, U32 code); + +// wrapper around caller's U32 filter(U32 code, struct _EXCEPTION_POINTERS*) +// filter function: capture a stacktrace, if possible, before forwarding the +// call to the caller's filter() function +template <typename FILTER> +U32 filter_(std::string& stacktrace, FILTER&& filter, +            U32 code, struct _EXCEPTION_POINTERS* exptrs) +{ +    // By the time the handler gets control, the stack has been unwound, +    // so report the stack trace now at filter() time. +    fill_stacktrace(stacktrace, code); +    return std::forward<FILTER>(filter)(code, exptrs); +} + +template <typename TRYCODE, typename FILTER, typename HANDLER> +auto catcher_inner(std::string& stacktrace, +                   TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler) +{ +    __try +    { +        return std::forward<TRYCODE>(trycode)(); +    } +    __except (filter_(stacktrace, +                      std::forward<FILTER>(filter), +                      GetExceptionCode(), GetExceptionInformation())) +    { +        return always_return<decltype(trycode())>( +            std::forward<HANDLER>(handler), GetExceptionCode(), stacktrace); +    } +} + +// triadic variant specifies try(), filter(U32, struct _EXCEPTION_POINTERS*), +// handler(U32, const std::string& stacktrace) +// stacktrace may or may not be available +template <typename TRYCODE, typename FILTER, typename HANDLER> +auto catcher(TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler) +{ +    // Construct and destroy this stacktrace string in the outer function +    // because we can't do either in the function with __try/__except. +    std::string stacktrace; +    return catcher_inner(stacktrace, +                         std::forward<TRYCODE>(trycode), +                         std::forward<FILTER>(filter), +                         std::forward<HANDLER>(handler)); +} -#if LL_WINDOWS +// common_filter() handles the typical case in which we want our handler +// clause to handle only Structured Exceptions rather than explicitly-thrown +// C++ exceptions +U32 common_filter(U32 code, struct _EXCEPTION_POINTERS*); + +// dyadic variant specifies try(), handler(U32, stacktrace), assumes common_filter() +template <typename TRYCODE, typename HANDLER> +auto catcher(TRYCODE&& trycode, HANDLER&& handler) +{ +    return catcher(std::forward<TRYCODE>(trycode), +                   common_filter, +                   std::forward<HANDLER>(handler)); +} + +// monadic variant specifies try(), assumes default filter and handler +template <typename TRYCODE> +auto catcher(TRYCODE&& trycode) +{ +    return catcher(std::forward<TRYCODE>(trycode), rethrow); +} + +[[noreturn]] void rethrow(U32 code, const std::string& stacktrace); + +#else  // not LL_WINDOWS ----------------------------------------------------- + +template <typename TRYCODE, typename FILTER, typename HANDLER> +auto catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&) +{ +    return std::forward<TRYCODE>(trycode)(); +} + +template <typename TRYCODE, typename HANDLER> +auto catcher(TRYCODE&& trycode, HANDLER&&) +{ +    return std::forward<TRYCODE>(trycode)(); +} + +template <typename TRYCODE> +auto catcher(TRYCODE&& trycode) +{ +    return std::forward<TRYCODE>(trycode)(); +} -// SEH exception filtering for use in __try __except -// Separates C++ exceptions from C SEH exceptions -// Todo: might be good idea to do some kind of seh_to_msc_wrapper(function, ARGS&&); -U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop); +#endif // not LL_WINDOWS ----------------------------------------------------- -#endif //LL_WINDOWS +} // namespace LL::seh +} // namespace LL  #endif /* ! defined(LL_LLEXCEPTION_H) */ diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 35d08d8de9..0e180d9915 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -896,19 +896,55 @@ public:  };  #elif LL_LINUX + +// *NOTE:Mani - eww, macros! srry. +#define LLPI_SET_INFO_STRING(llpi_id, cpuinfo_id) \ +        if (!cpuinfo[cpuinfo_id].empty()) \ +        { setInfo(llpi_id, cpuinfo[cpuinfo_id]);} + +#define LLPI_SET_INFO_INT(llpi_id, cpuinfo_id) \ +        {\ +            S32 result; \ +            if (!cpuinfo[cpuinfo_id].empty() \ +                && LLStringUtil::convertToS32(cpuinfo[cpuinfo_id], result)) \ +            { setInfo(llpi_id, result);} \ +        } +  const char CPUINFO_FILE[] = "/proc/cpuinfo"; -class LLProcessorInfoLinuxImpl : public LLProcessorInfoImpl -{ +class LLProcessorInfoLinuxImpl : public LLProcessorInfoImpl {  public: -    LLProcessorInfoLinuxImpl() -    { +    LLProcessorInfoLinuxImpl() {          get_proc_cpuinfo();      }      virtual ~LLProcessorInfoLinuxImpl() {} +  private: +    F64 getCPUMaxMHZ() +    { +        // Nicky: We just look into cpu0. In theory we could iterate over all cores +        // "/sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_max_freq" +        // But those should not fluctuate that much? +        std::ifstream fIn { "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" }; + +        if( !fIn.is_open() ) +            return 0.0; + +        std::string strLine; +        fIn >> strLine; +        if( strLine.empty() ) +            return 0.0l; + +        F64 mhz {}; +        if( !LLStringUtil::convertToF64(strLine, mhz ) ) +            return 0.0; + +        mhz = mhz / 1000.0; +        return mhz; +    } +      void get_proc_cpuinfo()      {          std::map< std::string, std::string > cpuinfo; @@ -937,30 +973,23 @@ private:                  std::string llinename(linename);                  LLStringUtil::toLower(llinename);                  std::string lineval( spacespot + 1, nlspot ); -                cpuinfo[ llinename ] = lineval; +                    cpuinfo[ llinename ] = lineval;              }              fclose(cpuinfo_fp);          }  # if LL_X86 -// *NOTE:Mani - eww, macros! srry. -#define LLPI_SET_INFO_STRING(llpi_id, cpuinfo_id) \ -        if (!cpuinfo[cpuinfo_id].empty()) \ -        { setInfo(llpi_id, cpuinfo[cpuinfo_id]);} - -#define LLPI_SET_INFO_INT(llpi_id, cpuinfo_id) \ -        {\ -            S32 result; \ -            if (!cpuinfo[cpuinfo_id].empty() \ -                && LLStringUtil::convertToS32(cpuinfo[cpuinfo_id], result)) \ -            { setInfo(llpi_id, result);} \ +        F64 mhzFromSys = getCPUMaxMHZ(); +        F64 mhzFromProc {}; +        if( !LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhzFromProc ) ) +            mhzFromProc = 0.0; +        if (mhzFromSys > 1.0 && mhzFromSys > mhzFromProc ) +        { +            setInfo( eFrequency, mhzFromSys );          } - -        F64 mhz; -        if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz) -            && 200.0 < mhz && mhz < 10000.0) +        else if (  200.0 < mhzFromProc && mhzFromProc < 10000.0)          { -            setInfo(eFrequency,(F64)(mhz)); +            setInfo(eFrequency,(F64)(mhzFromProc));          }          LLPI_SET_INFO_STRING(eBrandName, "model name"); @@ -970,7 +999,7 @@ private:          LLPI_SET_INFO_INT(eModel, "model"); -        S32 family = 0; +        S32 family{};          if (!cpuinfo["cpu family"].empty()              && LLStringUtil::convertToS32(cpuinfo["cpu family"], family))          { diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index fb6b6bd589..09bfb51cc3 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -161,7 +161,7 @@ LLSD ll_binary_from_string(const LLSD& sd)  char* ll_print_sd(const LLSD& sd)  {      const U32 bufferSize = 10 * 1024; -    static char buffer[bufferSize]; +    static char buffer[bufferSize + 1];      std::ostringstream stream;      //stream.rdbuf()->pubsetbuf(buffer, bufferSize);      stream << LLSDOStreamer<LLSDXMLFormatter>(sd); @@ -183,7 +183,7 @@ char* ll_pretty_print_sd_ptr(const LLSD* sd)  char* ll_pretty_print_sd(const LLSD& sd)  {      const U32 bufferSize = 100 * 1024; -    static char buffer[bufferSize]; +    static char buffer[bufferSize + 1];      std::ostringstream stream;      //stream.rdbuf()->pubsetbuf(buffer, bufferSize);      stream << LLSDOStreamer<LLSDXMLFormatter>(sd, LLSDFormatter::OPTIONS_PRETTY); diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 459fe0c0c8..d21afab050 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -525,57 +525,46 @@ const S32 LLOSInfo::getOSBitness() const      return mOSBitness;  } -//static -U32 LLOSInfo::getProcessVirtualSizeKB() -{ -    U32 virtual_size = 0; -#if LL_LINUX -#   define STATUS_SIZE 2048 -    LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); -    if (status_filep) -    { -        S32 numRead = 0; -        char buff[STATUS_SIZE];     /* Flawfinder: ignore */ +namespace { -        size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); -        buff[nbytes] = '\0'; +    U32 readFromProcStat( std::string entryName ) +    { +        U32 val{}; +#if LL_LINUX +        constexpr U32 STATUS_SIZE  = 2048; -        // All these guys return numbers in KB -        char *memp = strstr(buff, "VmSize:"); -        if (memp) +        LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); +        if (status_filep)          { -            numRead += sscanf(memp, "%*s %u", &virtual_size); +            char buff[STATUS_SIZE];     /* Flawfinder: ignore */ + +            size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); +            buff[nbytes] = '\0'; + +            // All these guys return numbers in KB +            char *memp = strstr(buff, entryName.c_str()); +            if (memp) +            { +                (void) sscanf(memp, "%*s %u", &val); +            } +            fclose(status_filep);          } -        fclose(status_filep); -    }  #endif -    return virtual_size; +        return val; +    } +  }  //static -U32 LLOSInfo::getProcessResidentSizeKB() +U32 LLOSInfo::getProcessVirtualSizeKB()  { -    U32 resident_size = 0; -#if LL_LINUX -    LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); -    if (status_filep != NULL) -    { -        S32 numRead = 0; -        char buff[STATUS_SIZE];     /* Flawfinder: ignore */ - -        size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); -        buff[nbytes] = '\0'; +    return readFromProcStat( "VmSize:" ); +} -        // All these guys return numbers in KB -        char *memp = strstr(buff, "VmRSS:"); -        if (memp) -        { -            numRead += sscanf(memp, "%*s %u", &resident_size); -        } -        fclose(status_filep); -    } -#endif -    return resident_size; +//static +U32 LLOSInfo::getProcessResidentSizeKB() +{ +    return readFromProcStat( "VmRSS:" );  }  //static diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index fa48bcdefd..3fb25b4cef 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -35,7 +35,7 @@  // causes Windows abdominal pain such that it later fails code-signing in some  // mysterious way. Entirely suppressing these LLLeap tests pushes the failure  // rate MUCH lower. Can we re-enable them with a smaller data size on Windows? -const size_t BUFFERED_LENGTH =  100*1024; +const size_t BUFFERED_LENGTH = 1023*1024;  #else // not Windows  const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte of data diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp index 3fadfa5334..ea5b0ee5fc 100644 --- a/indra/llcommon/tests/llstring_test.cpp +++ b/indra/llcommon/tests/llstring_test.cpp @@ -377,7 +377,7 @@ namespace tut      {          F32 value;          std::string str_val("2147483647"); //0x7FFFFFFF -        ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647); +        ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647.f);          str_val = "0";          ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0); @@ -399,7 +399,7 @@ namespace tut      {          F64 value;          std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF -        ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL); +        ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807.);          str_val = "0";          ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F);  | 
