From 4173fa1a9ef79c27d39d9ab9dbee46f109fbf510 Mon Sep 17 00:00:00 2001 From: AiraYumi Date: Sat, 30 Mar 2024 06:59:21 -0400 Subject: linux build fix --- indra/llcommon/llprocessor.cpp | 2 +- indra/llcommon/llsdutil.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 28f8bc2b93..7bca86119c 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -867,7 +867,7 @@ private: LLPI_SET_INFO_INT(eModel, "model"); - S32 family; + S32 family = 0; if (!cpuinfo["cpu family"].empty() && LLStringUtil::convertToS32(cpuinfo["cpu family"], family)) { diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index e98fc0285a..c3e7c544ec 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(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(sd, LLSDFormatter::OPTIONS_PRETTY); -- cgit v1.2.3 From a93344b4db2654e91b4daa9f2709daf448c03a8e Mon Sep 17 00:00:00 2001 From: AiraYumi Date: Mon, 1 Apr 2024 21:41:21 -0400 Subject: enable tracy for linux and build fix --- indra/llcommon/llprofiler.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprofiler.h b/indra/llcommon/llprofiler.h index af5e5777bf..0297bbe592 100644 --- a/indra/llcommon/llprofiler.h +++ b/indra/llcommon/llprofiler.h @@ -74,6 +74,10 @@ #define LL_PROFILER_CONFIGURATION LL_PROFILER_CONFIG_FAST_TIMER #endif +#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER + #include "Tracy.hpp" +#endif + extern thread_local bool gProfilerEnabled; #if defined(LL_PROFILER_CONFIGURATION) && (LL_PROFILER_CONFIGURATION > LL_PROFILER_CONFIG_NONE) @@ -84,7 +88,6 @@ extern thread_local bool gProfilerEnabled; // #define TRACY_NO_BROADCAST 1 // #define TRACY_ONLY_LOCALHOST 1 #define TRACY_ONLY_IPV4 1 - #include "Tracy.hpp" // Enable OpenGL profiling #define LL_PROFILER_ENABLE_TRACY_OPENGL 0 -- cgit v1.2.3 From 57d423745fd1d3d0ea6a0c69b869a20c27e27fc5 Mon Sep 17 00:00:00 2001 From: Nicky Dasmijn Date: Fri, 5 Apr 2024 19:25:02 +0200 Subject: Linux viewer (ReleaseOS) resurrection (#1099) Co-authored-by: AiraYumi --- indra/llcommon/llprocessor.cpp | 2 +- indra/llcommon/llsdutil.cpp | 1 + indra/llcommon/llsys.cpp | 133 +++++++++++++++++++---------------------- 3 files changed, 63 insertions(+), 73 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 7bca86119c..ce3b1160c0 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -867,7 +867,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 c3e7c544ec..7438524272 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -36,6 +36,7 @@ # include // for htonl #elif LL_LINUX # include +#pragma GCC diagnostic ignored "-Wstringop-truncation" // It's actually okay what happens here #elif LL_DARWIN # include #endif diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 938685bae6..988c74229c 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llsys.cpp * @brief Implementation of the basic system query functions. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -97,7 +97,7 @@ static const F32 MEM_INFO_THROTTLE = 20; static const F32 MEM_INFO_WINDOW = 10*60; LLOSInfo::LLOSInfo() : - mMajorVer(0), mMinorVer(0), mBuild(0), mOSVersionString("") + mMajorVer(0), mMinorVer(0), mBuild(0), mOSVersionString("") { #if LL_WINDOWS @@ -187,7 +187,7 @@ LLOSInfo::LLOSInfo() : if (NULL != pGNSI) //check if it has failed pGNSI(&si); //success else - GetSystemInfo(&si); //if it fails get regular system info + GetSystemInfo(&si); //if it fails get regular system info //(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load) // Try calling GetVersionEx using the OSVERSIONINFOEX structure. @@ -267,12 +267,12 @@ LLOSInfo::LLOSInfo() : LLStringUtil::trim(mOSString); #elif LL_DARWIN - + // Initialize mOSStringSimple to something like: // "Mac OS X 10.6.7" { const char * DARWIN_PRODUCT_NAME = "Mac OS X"; - + int64_t major_version, minor_version, bugfix_version = 0; if (LLGetDarwinOSInfo(major_version, minor_version, bugfix_version)) @@ -283,7 +283,7 @@ LLOSInfo::LLOSInfo() : std::stringstream os_version_string; os_version_string << DARWIN_PRODUCT_NAME << " " << mMajorVer << "." << mMinorVer << "." << mBuild; - + // Put it in the OS string we are compiling mOSStringSimple.append(os_version_string.str()); } @@ -292,12 +292,12 @@ LLOSInfo::LLOSInfo() : mOSStringSimple.append("Unable to collect OS info"); } } - + // Initialize mOSString to something like: // "Mac OS X 10.6.7 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386" struct utsname un; if(uname(&un) != -1) - { + { mOSString = mOSStringSimple; mOSString.append(" "); mOSString.append(un.sysname); @@ -312,9 +312,9 @@ LLOSInfo::LLOSInfo() : { mOSString = mOSStringSimple; } - + #elif LL_LINUX - + struct utsname un; if(uname(&un) != -1) { @@ -354,7 +354,7 @@ LLOSInfo::LLOSInfo() : if ( ll_regex_match(glibc_version, matched, os_version_parse) ) { LL_INFOS("AppInit") << "Using glibc version '" << glibc_version << "' as OS version" << LL_ENDL; - + std::string version_value; if ( matched[1].matched ) // Major version @@ -368,7 +368,7 @@ LLOSInfo::LLOSInfo() : else { LL_ERRS("AppInit") - << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION + << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION << "' returned true, but major version [1] did not match" << LL_ENDL; } @@ -384,7 +384,7 @@ LLOSInfo::LLOSInfo() : else { LL_ERRS("AppInit") - << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION + << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION << "' returned true, but minor version [1] did not match" << LL_ENDL; } @@ -410,7 +410,7 @@ LLOSInfo::LLOSInfo() : } #else - + struct utsname un; if(uname(&un) != -1) { @@ -510,57 +510,46 @@ const S32 LLOSInfo::getOSBitness() const return mOSBitness; } -//static -U32 LLOSInfo::getProcessVirtualSizeKB() -{ - U32 virtual_size = 0; +namespace { + + U32 readFromProcStat( std::string entryName ) + { + U32 val{}; #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 */ + constexpr U32 STATUS_SIZE = 2048; - size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); - buff[nbytes] = '\0'; + LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); + if (status_filep) + { + char buff[STATUS_SIZE]; /* Flawfinder: ignore */ - // All these guys return numbers in KB - char *memp = strstr(buff, "VmSize:"); - if (memp) - { - numRead += sscanf(memp, "%*s %u", &virtual_size); - } - fclose(status_filep); - } + 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); + } #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 @@ -578,7 +567,7 @@ bool LLOSInfo::is64Bit() #endif #else // ! LL_WINDOWS // we only build a 64-bit mac viewer and currently we don't build for linux at all - return true; + return true; #endif } @@ -1003,11 +992,11 @@ LLSD LLMemoryInfo::loadStatsMap() #elif LL_DARWIN const vm_size_t pagekb(vm_page_size / 1024); - + // // Collect the vm_stat's // - + { vm_statistics64_data_t vmstat; mach_msg_type_number_t vmstatCount = HOST_VM_INFO64_COUNT; @@ -1027,16 +1016,16 @@ LLSD LLMemoryInfo::loadStatsMap() stats.add("Page reactivations", vmstat.reactivations); stats.add("Page-ins", vmstat.pageins); stats.add("Page-outs", vmstat.pageouts); - + stats.add("Faults", vmstat.faults); stats.add("Faults copy-on-write", vmstat.cow_faults); - + stats.add("Cache lookups", vmstat.lookups); stats.add("Cache hits", vmstat.hits); - + stats.add("Page purgeable count", vmstat.purgeable_count); stats.add("Page purges", vmstat.purges); - + stats.add("Page speculative reads", vmstat.speculative_count); } } @@ -1048,7 +1037,7 @@ LLSD LLMemoryInfo::loadStatsMap() { task_events_info_data_t taskinfo; unsigned taskinfoSize = sizeof(taskinfo); - + if (task_info(mach_task_self(), TASK_EVENTS_INFO, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS) { LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; @@ -1063,8 +1052,8 @@ LLSD LLMemoryInfo::loadStatsMap() stats.add("Task unix system call count", taskinfo.syscalls_unix); stats.add("Task context switch count", taskinfo.csw); } - } - + } + // // Collect the basic task info // @@ -1350,8 +1339,8 @@ BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile) goto err; } } while(gzeof(src) == 0); - fclose(dst); - dst = NULL; + fclose(dst); + dst = NULL; if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */ retval = TRUE; err: -- cgit v1.2.3 From bbd6dfc21a1a6c07824fc81e0659a2c200581d3b Mon Sep 17 00:00:00 2001 From: AiraYumi Date: Tue, 9 Apr 2024 05:38:01 +0900 Subject: Some fixes to linux builds (#1144) * compile flag cleanup for linux * rollback indra/llcommon/llprofiler.h * use 3p fltk package * fix build gcc 13 (dangling-pointer) --- indra/llcommon/llprofiler.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprofiler.h b/indra/llcommon/llprofiler.h index 0297bbe592..af5e5777bf 100644 --- a/indra/llcommon/llprofiler.h +++ b/indra/llcommon/llprofiler.h @@ -74,10 +74,6 @@ #define LL_PROFILER_CONFIGURATION LL_PROFILER_CONFIG_FAST_TIMER #endif -#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER - #include "Tracy.hpp" -#endif - extern thread_local bool gProfilerEnabled; #if defined(LL_PROFILER_CONFIGURATION) && (LL_PROFILER_CONFIGURATION > LL_PROFILER_CONFIG_NONE) @@ -88,6 +84,7 @@ extern thread_local bool gProfilerEnabled; // #define TRACY_NO_BROADCAST 1 // #define TRACY_ONLY_LOCALHOST 1 #define TRACY_ONLY_IPV4 1 + #include "Tracy.hpp" // Enable OpenGL profiling #define LL_PROFILER_ENABLE_TRACY_OPENGL 0 -- cgit v1.2.3 From 37392be4171303db08a4842b7882b4cb758a8f8d Mon Sep 17 00:00:00 2001 From: Nicky Dasmijn Date: Tue, 9 Apr 2024 20:26:06 +0200 Subject: Update Linux media handling (#1146) * Enable CEF browser for Linux * Disable the update for Linux, we don't have that one right now * Update build_linux.yaml We need libpulse-dev for volume_catcher Linux * Add linux_volum_catcher* files * Enable OpenAL for Linux-ReleaseOS * Linux: Update OpenAL * Update SDL2 * Add libsndio-dev to the dependencies. * Update CEF to an official LL version * Remove dupe of emoji_shortcodes * Reording autobuild does because it can and wants to * Linux: Disable NDOF for the time being. After updating the ndof 3P needs to be rebuilt and we do not have a fresh one from LL yet. Forcefully undefine LIB_NDOF, it gets defined in the build variables no matter if it is safe to define. * Remove wrestling with mutliarch and LIBGL_DRIVERS_PATH * Remove tcmalloc snippet, tcmalloc is a very faint bad dream of the past * Putting out a warning this viewer ran on a x64 arch and then suggesting to install 32 bit compat packages makes no sense at all * CEF resources need to be in lib * It;'s okay to warn about missing plugins * Linux: CEF keyboard handling * Remove old gstreamer 0.10 implementation * Linux DSO loading always had been very peculiar due to macro magic. At least now it is peculiar shared magic with only one implementation. * Remove -fPIC. We get that one from LL_BUILD * /proc/cpuinfo is not reliable to detrmine the max CPU clock. Try to determine this by reading "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq". Only if this fails go back to /proc/cpuinfo * Cleanup * Cleanup common linker and compiler flags, make it more obvious which flags are for which OS/compiler * Switch to correct plugin file * Install libpulse-dev for volume catcher. * And the runner needs libsndio-dev as well. * check for runner.os=='linux'. matrix.os is the full name of the image (limux-large). --- indra/llcommon/llprocessor.cpp | 83 ++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 27 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index ce3b1160c0..4cdc3d7519 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -793,19 +793,55 @@ private: }; #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() - { - get_proc_cpuinfo(); - } + LLProcessorInfoLinuxImpl() { + get_proc_cpuinfo(); + } + + virtual ~LLProcessorInfoLinuxImpl() {} - 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; @@ -834,31 +870,24 @@ 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 mhz; - if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz) - && 200.0 < mhz && mhz < 10000.0) - { - setInfo(eFrequency,(F64)(mhz)); - } + F64 mhzFromSys = getCPUMaxMHZ(); + F64 mhzFromProc {}; + if( !LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhzFromProc ) ) + mhzFromProc = 0.0; + if (mhzFromSys > 1.0 && mhzFromSys > mhzFromProc ) + { + setInfo( eFrequency, mhzFromSys ); + } + else if ( 200.0 < mhzFromProc && mhzFromProc < 10000.0) + { + setInfo(eFrequency,(F64)(mhzFromProc)); + } LLPI_SET_INFO_STRING(eBrandName, "model name"); LLPI_SET_INFO_STRING(eVendor, "vendor_id"); -- cgit v1.2.3 From 8f0c41c2e5d5c66df5e975b743140112494c4a7a Mon Sep 17 00:00:00 2001 From: Nicky Dasmijn Date: Mon, 15 Apr 2024 22:59:16 +0200 Subject: Chore/pragma gcc cleansweep (#1226) * Remove all GCC warning suppression pragmas. * For Linux just just raise(SIGSEGV) as the crash driver. This has a much higher chance of the compiler understanding out intent and figuring out we end the program here. * Remove -Wno-stringop-overflow and -Wno-stringop-truncation from GCC_WARNINGS. After calling raise(SIGSEGV) as the crash driver I saw no issue with those warnings anymore After removing thoses GCC pragmas there is also no need for clang -Wno-unknown-warning-option anymore. * Remove CMakePresets from this PR. * Remove Lindens from comments :) --- indra/llcommon/llerror.h | 103 ++++++++++++++++++++++++-------------------- indra/llcommon/llsdutil.cpp | 23 +++++----- 2 files changed, 68 insertions(+), 58 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 6f6b349cf5..a70b5cef3a 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -1,4 +1,4 @@ -/** +/** * @file llerror.h * @date December 2006 * @brief error message system @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -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 +#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 @@ -103,12 +108,12 @@ const int LL_ERR_NOERR = 0; /** Error Logging Facility Information for most users: - + Code can log messages with constructions like this: - + LL_INFOS("StringTag") << "request to fizzbip agent " << agent_id << " denied due to timeout" << LL_ENDL; - + Messages can be logged to one of four increasing levels of concern, using one of four "streams": @@ -116,45 +121,45 @@ const int LL_ERR_NOERR = 0; LL_INFOS("StringTag") - informational messages that are normal shown LL_WARNS("StringTag") - warning messages that signal a problem LL_ERRS("StringTag") - error messages that are major, unrecoverable failures - + The later (LL_ERRS("StringTag")) automatically crashes the process after the message is logged. - + Note that these "streams" are actually #define magic. Rules for use: * they cannot be used as normal streams, only to start a message * messages written to them MUST be terminated with LL_ENDL * between the opening and closing, the << operator is indeed writing onto a std::ostream, so all conversions and stream formating are available - + These messages are automatically logged with function name, and (if enabled) file and line of the message. (Note: Existing messages that already include the function name don't get name printed twice.) - + If you have a class, adding LOG_CLASS line to the declaration will cause all messages emitted from member functions (normal and static) to be tagged with the proper class name as well as the function name: - + class LLFoo { LOG_CLASS(LLFoo); public: ... }; - + void LLFoo::doSomething(int i) { if (i > 100) { - LL_WARNS("FooBarTag") << "called with a big value for i: " << i << LL_ENDL; + LL_WARNS("FooBarTag") << "called with a big value for i: " << i << LL_ENDL; } ... } - + will result in messages like: - + WARN #FooBarTag# llcommon/llfoo(100) LLFoo::doSomething : called with a big value for i: 283 - + the syntax is: SPACE SPACE SPACE SPACE SPACE COLON SPACE @@ -169,7 +174,7 @@ const int LL_ERR_NOERR = 0; A copy of that file named logcontrol-dev.xml can be made in the users personal settings directory; that will override the installed default file. See the logcontrol.xml file or http://wiki.secondlife.com/wiki/Logging_System_Overview for configuration details. - + Lastly, logging is now very efficient in both compiled code and execution when skipped. There is no need to wrap messages, even debugging ones, in #ifdef _DEBUG constructs. LL_DEBUGS("StringTag") messages are compiled into all builds, @@ -182,12 +187,12 @@ namespace LLError { LEVEL_ALL = 0, // used to indicate that all messages should be logged - + LEVEL_DEBUG = 0, LEVEL_INFO = 1, LEVEL_WARN = 2, LEVEL_ERROR = 3, // used to be called FATAL - + LEVEL_NONE = 4 // not really a level // used to indicate that no messages should be logged @@ -223,13 +228,13 @@ namespace LLError // Represents a specific place in the code where a message is logged // This is public because it is used by the macros below. It is not // intended for public use. - CallSite(ELevel level, - const char* file, + CallSite(ELevel level, + const char* file, int line, - const std::type_info& class_info, - const char* function, - bool print_once, - const char** tags, + const std::type_info& class_info, + const char* function, + bool print_once, + const char** tags, size_t tag_count); ~CallSite(); @@ -238,16 +243,16 @@ namespace LLError bool shouldLog(); #else // LL_LIBRARY_INCLUDE bool shouldLog() - { - return mCached - ? mShouldLog - : Log::shouldLog(*this); + { + return mCached + ? mShouldLog + : Log::shouldLog(*this); } // this member function needs to be in-line for efficiency #endif // LL_LIBRARY_INCLUDE - + void invalidate(); - + // these describe the call site and never change const ELevel mLevel; const char* const mFile; @@ -263,22 +268,22 @@ namespace LLError mTagString; bool mCached, mShouldLog; - + friend class Log; }; - - + + class End { }; inline std::ostream& operator<<(std::ostream& s, const End&) { return s; } // used to indicate the end of a message - + class LL_COMMON_API NoClassInfo { }; // used to indicate no class info known for logging //LLCallStacks keeps track of call stacks and output the call stacks to log file // - //Note: to be simple, efficient and necessary to keep track of correct call stacks, + //Note: to be simple, efficient and necessary to keep track of correct call stacks, //LLCallStacks is designed not to be thread-safe. //so try not to use it in multiple parallel threads at same time. //Used in a single thread at a time is fine. @@ -287,8 +292,8 @@ namespace LLError private: typedef std::vector StringVector; static StringVector sBuffer ; - - public: + + public: static void push(const char* function, const int line) ; static void insert(std::ostream& out, const char* function, const int line) ; static void print() ; @@ -326,7 +331,7 @@ namespace LLError }; } -//this is cheaper than llcallstacks if no need to output other variables to call stacks. +//this is cheaper than llcallstacks if no need to output other variables to call stacks. #define LL_PUSH_CALLSTACKS() LLError::LLCallStacks::push(__FUNCTION__, __LINE__) #define llcallstacks \ @@ -341,7 +346,7 @@ namespace LLError } #define LL_CLEAR_CALLSTACKS() LLError::LLCallStacks::clear() -#define LL_PRINT_CALLSTACKS() LLError::LLCallStacks::print() +#define LL_PRINT_CALLSTACKS() LLError::LLCallStacks::print() /* Class type information for logging @@ -350,7 +355,7 @@ namespace LLError #define LOG_CLASS(s) typedef s _LL_CLASS_TO_LOG // Declares class to tag logged messages with. // See top of file for example of how to use this - + typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; // Outside a class declaration, or in class without LOG_CLASS(), this // typedef causes the messages to not be associated with any class. @@ -392,7 +397,7 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; //Use this construct if you need to do computation in the middle of a //message: -// +// // LL_INFOS("AgentGesture") << "the agent " << agend_id; // switch (f) // { @@ -401,17 +406,23 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; // case FOP_SAYS: LL_CONT << "says " << message; break; // } // LL_CONT << " for " << t << " seconds" << LL_ENDL; -// +// //Such computation is done iff the message will be logged. #define LL_CONT _out #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(); \ @@ -510,7 +521,7 @@ LL_ENDL; LL_DEBUGS("SomeTag") performs the locking and map-searching ONCE, then caches the result in a static variable. -*/ +*/ // used by LLERROR_CRASH void crashdriver(void (*)(int*)); diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index 7438524272..4d5fb0d818 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llsdutil.cpp * @author Phoenix * @date 2006-05-24 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,7 +36,6 @@ # include // for htonl #elif LL_LINUX # include -#pragma GCC diagnostic ignored "-Wstringop-truncation" // It's actually okay what happens here #elif LL_DARWIN # include #endif @@ -325,7 +324,7 @@ BOOL compare_llsd_with_template( return TRUE; } -// filter_llsd_with_template() is a direct clone (copy-n-paste) of +// filter_llsd_with_template() is a direct clone (copy-n-paste) of // compare_llsd_with_template with the following differences: // (1) bool vs BOOL return types // (2) A map with the key value "*" is a special value and maps any key in the @@ -387,7 +386,7 @@ bool filter_llsd_with_template( else { // Traditional compare_llsd_with_template matching - + for (template_iter = template_llsd.beginArray(); template_iter != template_llsd.endArray() && test_iter != llsd_to_test.endArray(); @@ -418,7 +417,7 @@ bool filter_llsd_with_template( else if (llsd_to_test.isMap()) { resultant_llsd = LLSD::emptyMap(); - + //now we loop over the keys of the two maps //any excess is taken from the template //excess is ignored in the test @@ -465,7 +464,7 @@ bool filter_llsd_with_template( { LLSD sub_value; LLSD::map_const_iterator test_iter; - + for (test_iter = llsd_to_test.beginMap(); test_iter != llsd_to_test.endMap(); ++test_iter) @@ -945,9 +944,9 @@ LLSD drill(const LLSD& blob, const LLSD& path) } // namespace llsd -// Construct a deep partial clone of of an LLSD object. primitive types share +// Construct a deep partial clone of of an LLSD object. primitive types share // references, however maps, arrays and binary objects are duplicated. An optional -// filter may be include to exclude/include keys in a map. +// filter may be include to exclude/include keys in a map. LLSD llsd_clone(LLSD value, LLSD filter) { LL_PROFILE_ZONE_SCOPED -- cgit v1.2.3 From 1b6e2ef62cec9608d160ea25d99080f0e2964ee5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 16 May 2024 13:30:14 -0400 Subject: On Windows, defend test.cpp against structured exceptions too. Since August 2023, we've seen occasional GitHub Windows build test runs terminate with 0xC00000FD: stack overflow. We've usually responded by bumping up the default coroutine stack size. On closer examination, it's always llleap_test.cpp that blows up that way -- and llleap_test.cpp doesn't appear to use coroutines at all. So apparently we've been consuming more address space for ALL viewer coroutines without actually addressing the problem. Reset the default coroutine stack size to where it was before we started bumping it up in response to these llleap_test.cpp stack overflow failures. Note that LLCoros already catches and reports Windows structured exceptions, underscoring that the observed stack overflow is not from within a coroutine. While at it, restore the Windows llleap_test.cpp data volume to match Posix. We think the problem that led to reducing that data volume was an APR bug, which we hope has been fixed. Equip test.cpp, the test driver program for all our TUT unit and integration tests, with a Windows structured exception handler. Try to treat a Windows structured exception as a test failure -- instead of silently terminating with 0xC00000FD. Moreover, when a structured exception occurs, output a stack trace so we can try to track it down. --- indra/llcommon/llcoros.cpp | 4 ++-- indra/llcommon/tests/llleap_test.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index aa8eca7d90..a70e3d9ae7 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"); 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 -- cgit v1.2.3 From e980dffd5c08bdaad745e47d062c4e7c4c968a6e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 16 May 2024 14:48:14 -0400 Subject: Linux clang++ doesn't like implicit conversion of extreme ints. Try specifying the literal constants in the type we're comparing to. --- indra/llcommon/tests/llstring_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') 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); -- cgit v1.2.3 From 71d777ea126e7f02cb46c11bdb606094ca06f75c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 24 May 2024 17:34:04 -0400 Subject: Promote seh_catcher() et al. to llexception.{h,cpp} for general use. --- indra/llcommon/llcoros.cpp | 51 +------------------------ indra/llcommon/llexception.cpp | 50 ++++++++++++++++++++---- indra/llcommon/llexception.h | 86 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 126 insertions(+), 61 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index a70e3d9ae7..f3943b7138 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -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); + seh_catcher(callable); } catch (const Stop& exc) { diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index c0154a569f..74b33f1e3b 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -15,7 +15,12 @@ #include "llexception.h" // STL headers // std headers +#include +#include #include +#if LL_WINDOWS +#include +#endif // LL_WINDOWS // external library headers #include #include @@ -29,7 +34,6 @@ // On Windows, header-only implementation causes macro collisions -- use // prebuilt library #define BOOST_STACKTRACE_LINK -#include #endif // LL_WINDOWS #include @@ -94,15 +98,34 @@ 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) +U32 ll_seh_filter( + std::string& stacktrace, + std::function filter, + U32 code, + struct _EXCEPTION_POINTERS* exception_infop) { - 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; + // By the time the handler gets control, the stack has been unwound, + // so report the stack trace now at filter() time. + // Even though stack overflow is a problem we would very much like to + // diagnose, calling another function when the stack is already blown only + // terminates us faster. + if (code == STATUS_STACK_FULL) + { + stacktrace = "(stack overflow, no traceback)"; + } + else + { + stacktrace = boost::stacktrace::stacktrace().to_string(); + } + + return filter(code, exception_infop); +} +U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS*) +{ if (code == STATUS_MSC_EXCEPTION) { // C++ exception, go on @@ -110,9 +133,20 @@ U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) } else { - // handle it + // This is a non-C++ exception, e.g. hardware check. return EXCEPTION_EXECUTE_HANDLER; } } +void 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..3e50678b44 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -102,14 +102,94 @@ 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 std::runtime_error +{ + Windows_SEH_exception(const std::string& what): std::runtime_error(what) {} +}; + +#if LL_WINDOWS //------------------------------------------------------------- + +#include + +// triadic variant specifies try(), filter(U32, struct _EXCEPTION_POINTERS*), +// handler(U32, const std::string& stacktrace) +// stacktrace may or may not be available +template +auto seh_catcher(TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler) +{ + // don't try to construct a std::function at the moment of Structured Exception + std::function + filter_function(std::forward(filter)); + std::string stacktrace; + __try + { + return std::forward(trycode)(); + } + __except (ll_seh_filter( + stacktrace, + filter_function, + GetExceptionCode(), + GetExceptionInformation())) + { + return std::forward(handler)(GetExceptionCode(), stacktrace); + } +} + +// dyadic variant specifies try(), handler(U32, stacktrace), assumes default filter +template +auto seh_catcher(TRYCODE&& trycode, HANDLER&& handler) +{ + return seh_catcher( + std::forward(trycode), + seh_filter, + std::forward(handler)); +} -#if LL_WINDOWS +// monadic variant specifies try(), assumes default filter and handler +template +auto seh_catcher(TRYCODE&& trycode) +{ + return seh_catcher( + std::forward(trycode), + seh_filter, + seh_rethrow); +} // 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); +U32 ll_seh_filter( + std::string& stacktrace, + std::function filter, + U32 code, + struct _EXCEPTION_POINTERS* exception_infop); +U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop); +void seh_rethrow(U32 code, const std::string& stacktrace); + +#else // not LL_WINDOWS ----------------------------------------------------- + +template +auto seh_catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&) +{ + return std::forward(trycode)(); +} + +template +auto seh_catcher(TRYCODE&& trycode, HANDLER&&) +{ + return std::forward(trycode)(); +} + +template +auto seh_catcher(TRYCODE&& trycode) +{ + return std::forward(trycode)(); +} -#endif //LL_WINDOWS +#endif // not LL_WINDOWS ----------------------------------------------------- #endif /* ! defined(LL_LLEXCEPTION_H) */ -- cgit v1.2.3 From 5ed8df22cd59680a685c4ada7daa5555bf59d4fe Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 28 May 2024 13:22:05 -0400 Subject: Fix up llexception.h's cross-platform SEH wrapper. Introduce AlwaysReturn specialization, which always discards any result of calling the specified callable with specified args. Derive new Windows_SEH_exception from LLException, not std::runtime_error. Put the various SEH functions in LL::seh nested namespace, e.g. LL::seh::catcher() as the primary API. Break out more levels of Windows SEH handler to work around the restrictions on functions containing __try/__except. The triadic catcher() overload now does little save declare a std::string stacktrace before forwarding the call to catcher_inner(), passing a reference to stacktrace along with the trycode, filter and handler functions. catcher_inner() accepts the stacktrace and the three function template arguments. It contains the __try/__except logic. It calls a new filter_() wrapper template, which calls fill_stacktrace() before forwarding the call to the caller's filter function. fill_stacktrace(), in the .cpp file, contains the logic to populate the stacktrace string -- unless the Structured Exception is stack overflow, in which case it puts an explanatory string instead. catcher_inner()'s __except clause passes not only the code, but also the stacktrace string, to the caller's handler function. It wraps the caller's handler function in always_return(), where rtype is the type returned by the trycode function. This allows a handler to return a value, while also supporting the void handler case, e.g. one that throws a C++ exception. (This is why we need AlwaysReturn: some trycode() functions are themselves void.) For the dyadic catcher() overload, introduce common_filter() containing the logic to distinguish a C++ exception from any other kind of Structured Exception. The fact that the stacktrace is captured before the filter function is called should permit capturing a stacktrace for a C++ exception as well as for most other Structured Exceptions. As before, the monadic catcher() overload supplies the rethrow() handler, in the .cpp file. Change existing calls from seh_catcher() to LL::seh::catcher(). --- indra/llcommon/always_return.h | 16 +++++++ indra/llcommon/llcoros.cpp | 2 +- indra/llcommon/llexception.cpp | 24 ++++------ indra/llcommon/llexception.h | 104 +++++++++++++++++++++++++---------------- 4 files changed, 88 insertions(+), 58 deletions(-) (limited to 'indra/llcommon') 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 + template <> + struct AlwaysReturn + { + public: + AlwaysReturn() {} + + // callable returns a type not convertible to DESIRED, return default + template + void operator()(CALLABLE&& callable, ARGS&&... args) + { + // discard whatever callable(args) returns + std::forward(callable)(std::forward(args)...); + } + }; + /** * always_return(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 f3943b7138..7512bac88d 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -299,7 +299,7 @@ void LLCoros::toplevel(std::string name, callable_t callable) // run the code the caller actually wants in the coroutine try { - seh_catcher(callable); + LL::seh::catcher(callable); } catch (const Stop& exc) { diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index 74b33f1e3b..107fdc2b2d 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -101,44 +101,36 @@ void annotate_exception_(boost::exception& exc) static constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific static constexpr U32 STATUS_STACK_FULL = 0xC00000FD; -U32 ll_seh_filter( - std::string& stacktrace, - std::function filter, - U32 code, - struct _EXCEPTION_POINTERS* exception_infop) +void LL::seh::fill_stacktrace(std::string& stacktrace, U32 code) { - // By the time the handler gets control, the stack has been unwound, - // so report the stack trace now at filter() time. - // Even though stack overflow is a problem we would very much like to - // diagnose, calling another function when the stack is already blown only - // terminates us faster. + // 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 = boost::stacktrace::stacktrace().to_string(); + stacktrace = to_string(boost::stacktrace::stacktrace()); } - - return filter(code, exception_infop); } -U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS*) +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 { // This is a non-C++ exception, e.g. hardware check. + // Pass control into the handler block. return EXCEPTION_EXECUTE_HANDLER; } } -void seh_rethrow(U32 code, const std::string& stacktrace) +void LL::seh::rethrow(U32 code, const std::string& stacktrace) { std::ostringstream out; out << "Windows exception 0x" << std::hex << code; diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 3e50678b44..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 #include #include @@ -106,90 +107,111 @@ 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 std::runtime_error +struct Windows_SEH_exception: public LLException { - Windows_SEH_exception(const std::string& what): std::runtime_error(what) {} + Windows_SEH_exception(const std::string& what): LLException(what) {} }; +namespace LL +{ +namespace seh +{ + #if LL_WINDOWS //------------------------------------------------------------- -#include +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 +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)(code, exptrs); +} -// triadic variant specifies try(), filter(U32, struct _EXCEPTION_POINTERS*), -// handler(U32, const std::string& stacktrace) -// stacktrace may or may not be available template -auto seh_catcher(TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler) +auto catcher_inner(std::string& stacktrace, + TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler) { - // don't try to construct a std::function at the moment of Structured Exception - std::function - filter_function(std::forward(filter)); - std::string stacktrace; __try { return std::forward(trycode)(); } - __except (ll_seh_filter( - stacktrace, - filter_function, - GetExceptionCode(), - GetExceptionInformation())) + __except (filter_(stacktrace, + std::forward(filter), + GetExceptionCode(), GetExceptionInformation())) { - return std::forward(handler)(GetExceptionCode(), stacktrace); + return always_return( + std::forward(handler), GetExceptionCode(), stacktrace); } } -// dyadic variant specifies try(), handler(U32, stacktrace), assumes default filter +// triadic variant specifies try(), filter(U32, struct _EXCEPTION_POINTERS*), +// handler(U32, const std::string& stacktrace) +// stacktrace may or may not be available +template +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), + std::forward(filter), + std::forward(handler)); +} + +// 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 -auto seh_catcher(TRYCODE&& trycode, HANDLER&& handler) +auto catcher(TRYCODE&& trycode, HANDLER&& handler) { - return seh_catcher( - std::forward(trycode), - seh_filter, - std::forward(handler)); + return catcher(std::forward(trycode), + common_filter, + std::forward(handler)); } // monadic variant specifies try(), assumes default filter and handler template -auto seh_catcher(TRYCODE&& trycode) +auto catcher(TRYCODE&& trycode) { - return seh_catcher( - std::forward(trycode), - seh_filter, - seh_rethrow); + return catcher(std::forward(trycode), rethrow); } -// 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 ll_seh_filter( - std::string& stacktrace, - std::function filter, - U32 code, - struct _EXCEPTION_POINTERS* exception_infop); -U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop); -void seh_rethrow(U32 code, const std::string& stacktrace); +[[noreturn]] void rethrow(U32 code, const std::string& stacktrace); #else // not LL_WINDOWS ----------------------------------------------------- template -auto seh_catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&) +auto catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&) { return std::forward(trycode)(); } template -auto seh_catcher(TRYCODE&& trycode, HANDLER&&) +auto catcher(TRYCODE&& trycode, HANDLER&&) { return std::forward(trycode)(); } template -auto seh_catcher(TRYCODE&& trycode) +auto catcher(TRYCODE&& trycode) { return std::forward(trycode)(); } #endif // not LL_WINDOWS ----------------------------------------------------- +} // namespace LL::seh +} // namespace LL + #endif /* ! defined(LL_LLEXCEPTION_H) */ -- cgit v1.2.3