diff options
Diffstat (limited to 'indra')
41 files changed, 1540 insertions, 292 deletions
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 2c974fb4ff..0266239454 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -171,7 +171,10 @@ if (LINUX) add_definitions(-fvisibility=hidden) # don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work. Sigh! The viewer doesn't need to catch SIGCHLD anyway. add_definitions(-DLL_IGNORE_SIGCHLD) - add_definitions(-march=pentium4 -mfpmath=sse) + if (WORD_SIZE EQUAL 32) + add_definitions(-march=pentium4) + endif (WORD_SIZE EQUAL 32) + add_definitions(-mfpmath=sse) #add_definitions(-ftree-vectorize) # THIS CRASHES GCC 3.1-3.2 if (NOT STANDALONE) # this stops us requiring a really recent glibc at runtime diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp index 8beae555fb..99d0ad7e14 100644 --- a/indra/linux_crash_logger/linux_crash_logger.cpp +++ b/indra/linux_crash_logger/linux_crash_logger.cpp @@ -24,16 +24,24 @@ * $/LicenseInfo$ */ +#include "linden_common.h" #include "llcrashloggerlinux.h" int main(int argc, char **argv) { + llinfos << "Starting crash reporter." << llendl; + LLCrashLoggerLinux app; app.parseCommandOptions(argc, argv); - app.init(); + + if (! app.init()) + { + llwarns << "Unable to initialize application." << llendl; + return 1; + } + app.mainLoop(); app.cleanup(); + llinfos << "Crash reporter finished normally." << llendl; return 0; } - - diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp index 7449c6426f..7316717193 100644 --- a/indra/linux_crash_logger/llcrashloggerlinux.cpp +++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp @@ -30,8 +30,6 @@ #include "linden_common.h" -#include "boost/tokenizer.hpp" - #include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME #include "llerror.h" #include "llfile.h" diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index d0f287657e..0745696ef3 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -387,8 +387,6 @@ const S32 MAP_SIM_RETURN_NULL_SIMS = 0x00010000; const S32 MAP_SIM_PRELUDE = 0x00020000; // Crash reporter behavior -const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml"; -const char* const CRASH_BEHAVIOR_SETTING = "CrashSubmitBehavior"; const S32 CRASH_BEHAVIOR_ASK = 0; const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1; const S32 CRASH_BEHAVIOR_NEVER_SEND = 2; diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index e8616a9be6..99e61433c6 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -1,6 +1,6 @@ /** * @file llsys.cpp - * @brief Impelementation of the basic system query functions. + * @brief Implementation of the basic system query functions. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code @@ -24,6 +24,10 @@ * $/LicenseInfo$ */ +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + #include "linden_common.h" #include "llsys.h" @@ -36,22 +40,43 @@ #endif #include "llprocessor.h" +#include "llerrorcontrol.h" +#include "llevents.h" +#include "lltimer.h" +#include "llsdserialize.h" +#include "llsdutil.h" +#include <boost/bind.hpp> +#include <boost/circular_buffer.hpp> +#include <boost/regex.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/range.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/type_traits/is_integral.hpp> +#include <boost/type_traits/is_float.hpp> + +using namespace llsd; #if LL_WINDOWS # define WIN32_LEAN_AND_MEAN # include <winsock2.h> # include <windows.h> +# include <psapi.h> // GetPerformanceInfo() et al. #elif LL_DARWIN # include <errno.h> # include <sys/sysctl.h> # include <sys/utsname.h> # include <stdint.h> # include <Carbon/Carbon.h> +# include <sys/wait.h> +# include <string.h> +# include <stdexcept> #elif LL_LINUX # include <errno.h> # include <sys/utsname.h> # include <unistd.h> # include <sys/sysinfo.h> +# include <stdexcept> const char MEMINFO_FILE[] = "/proc/meminfo"; #elif LL_SOLARIS # include <stdio.h> @@ -70,6 +95,15 @@ extern int errno; static const S32 CPUINFO_BUFFER_SIZE = 16383; LLCPUInfo gSysCPU; +// Don't log memory info any more often than this. It also serves as our +// framerate sample size. +static const F32 MEM_INFO_THROTTLE = 20; +// Sliding window of samples. We intentionally limit the length of time we +// remember "the slowest" framerate because framerate is very slow at login. +// If we only triggered FrameWatcher logging when the session framerate +// dropped below the login framerate, we'd have very little additional data. +static const F32 MEM_INFO_WINDOW = 10*60; + #if LL_WINDOWS #ifndef DLLVERSIONINFO typedef struct _DllVersionInfo @@ -613,8 +647,78 @@ void LLCPUInfo::stream(std::ostream& s) const s << "->mCPUString: " << mCPUString << std::endl; } +// Helper class for LLMemoryInfo: accumulate stats in the form we store for +// LLMemoryInfo::getStatsMap(). +class Stats +{ +public: + Stats(): + mStats(LLSD::emptyMap()) + {} + + // Store every integer type as LLSD::Integer. + template <class T> + void add(const LLSD::String& name, const T& value, + typename boost::enable_if<boost::is_integral<T> >::type* = 0) + { + mStats[name] = LLSD::Integer(value); + } + + // Store every floating-point type as LLSD::Real. + template <class T> + void add(const LLSD::String& name, const T& value, + typename boost::enable_if<boost::is_float<T> >::type* = 0) + { + mStats[name] = LLSD::Real(value); + } + + // Hope that LLSD::Date values are sufficiently unambiguous. + void add(const LLSD::String& name, const LLSD::Date& value) + { + mStats[name] = value; + } + + LLSD get() const { return mStats; } + +private: + LLSD mStats; +}; + +// Wrap boost::regex_match() with a function that doesn't throw. +template <typename S, typename M, typename R> +static bool regex_match_no_exc(const S& string, M& match, const R& regex) +{ + try + { + return boost::regex_match(string, match, regex); + } + catch (const std::runtime_error& e) + { + LL_WARNS("LLMemoryInfo") << "error matching with '" << regex.str() << "': " + << e.what() << ":\n'" << string << "'" << LL_ENDL; + return false; + } +} + +// Wrap boost::regex_search() with a function that doesn't throw. +template <typename S, typename M, typename R> +static bool regex_search_no_exc(const S& string, M& match, const R& regex) +{ + try + { + return boost::regex_search(string, match, regex); + } + catch (const std::runtime_error& e) + { + LL_WARNS("LLMemoryInfo") << "error searching with '" << regex.str() << "': " + << e.what() << ":\n'" << string << "'" << LL_ENDL; + return false; + } +} + LLMemoryInfo::LLMemoryInfo() { + refresh(); } #if LL_WINDOWS @@ -638,11 +742,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB) U32 LLMemoryInfo::getPhysicalMemoryKB() const { #if LL_WINDOWS - MEMORYSTATUSEX state; - state.dwLength = sizeof(state); - GlobalMemoryStatusEx(&state); - - return LLMemoryAdjustKBResult((U32)(state.ullTotalPhys >> 10)); + return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"].asInteger()); #elif LL_DARWIN // This might work on Linux as well. Someone check... @@ -690,12 +790,82 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb) { #if LL_WINDOWS - MEMORYSTATUSEX state; - state.dwLength = sizeof(state); - GlobalMemoryStatusEx(&state); + // Sigh, this shouldn't be a static method, then we wouldn't have to + // reload this data separately from refresh() + LLSD statsMap(loadStatsMap()); + + avail_physical_mem_kb = statsMap["Avail Physical KB"].asInteger(); + avail_virtual_mem_kb = statsMap["Avail Virtual KB"].asInteger(); + +#elif LL_DARWIN + // mStatsMap is derived from vm_stat, look for (e.g.) "kb free": + // $ vm_stat + // Mach Virtual Memory Statistics: (page size of 4096 bytes) + // Pages free: 462078. + // Pages active: 142010. + // Pages inactive: 220007. + // Pages wired down: 159552. + // "Translation faults": 220825184. + // Pages copy-on-write: 2104153. + // Pages zero filled: 167034876. + // Pages reactivated: 65153. + // Pageins: 2097212. + // Pageouts: 41759. + // Object cache: 841598 hits of 7629869 lookups (11% hit rate) + avail_physical_mem_kb = -1 ; + avail_virtual_mem_kb = -1 ; - avail_physical_mem_kb = (U32)(state.ullAvailPhys/1024) ; - avail_virtual_mem_kb = (U32)(state.ullAvailVirtual/1024) ; +#elif LL_LINUX + // mStatsMap is derived from MEMINFO_FILE: + // $ cat /proc/meminfo + // MemTotal: 4108424 kB + // MemFree: 1244064 kB + // Buffers: 85164 kB + // Cached: 1990264 kB + // SwapCached: 0 kB + // Active: 1176648 kB + // Inactive: 1427532 kB + // Active(anon): 529152 kB + // Inactive(anon): 15924 kB + // Active(file): 647496 kB + // Inactive(file): 1411608 kB + // Unevictable: 16 kB + // Mlocked: 16 kB + // HighTotal: 3266316 kB + // HighFree: 721308 kB + // LowTotal: 842108 kB + // LowFree: 522756 kB + // SwapTotal: 6384632 kB + // SwapFree: 6384632 kB + // Dirty: 28 kB + // Writeback: 0 kB + // AnonPages: 528820 kB + // Mapped: 89472 kB + // Shmem: 16324 kB + // Slab: 159624 kB + // SReclaimable: 145168 kB + // SUnreclaim: 14456 kB + // KernelStack: 2560 kB + // PageTables: 5560 kB + // NFS_Unstable: 0 kB + // Bounce: 0 kB + // WritebackTmp: 0 kB + // CommitLimit: 8438844 kB + // Committed_AS: 1271596 kB + // VmallocTotal: 122880 kB + // VmallocUsed: 65252 kB + // VmallocChunk: 52356 kB + // HardwareCorrupted: 0 kB + // HugePages_Total: 0 + // HugePages_Free: 0 + // HugePages_Rsvd: 0 + // HugePages_Surp: 0 + // Hugepagesize: 2048 kB + // DirectMap4k: 434168 kB + // DirectMap2M: 477184 kB + // (could also run 'free', but easier to read a file than run a program) + avail_physical_mem_kb = -1 ; + avail_virtual_mem_kb = -1 ; #else //do not know how to collect available memory info for other systems. @@ -708,56 +878,389 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v void LLMemoryInfo::stream(std::ostream& s) const { + // We want these memory stats to be easy to grep from the log, along with + // the timestamp. So preface each line with the timestamp and a + // distinctive marker. Without that, we'd have to search the log for the + // introducer line, then read subsequent lines, etc... + std::string pfx(LLError::utcTime() + " <mem> "); + + // Max key length + size_t key_width(0); + BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap)) + { + size_t len(pair.first.length()); + if (len > key_width) + { + key_width = len; + } + } + + // Now stream stats + BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap)) + { + s << pfx << std::setw(key_width+1) << (pair.first + ':') << ' '; + LLSD value(pair.second); + if (value.isInteger()) + s << std::setw(12) << value.asInteger(); + else if (value.isReal()) + s << std::fixed << std::setprecision(1) << value.asReal(); + else if (value.isDate()) + value.asDate().toStream(s); + else + s << value; // just use default LLSD formatting + s << std::endl; + } +} + +LLSD LLMemoryInfo::getStatsMap() const +{ + return mStatsMap; +} + +LLMemoryInfo& LLMemoryInfo::refresh() +{ + mStatsMap = loadStatsMap(); + + LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n"; + LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT); + LL_ENDL; + + return *this; +} + +LLSD LLMemoryInfo::loadStatsMap() +{ + // This implementation is derived from stream() code (as of 2011-06-29). + Stats stats; + + // associate timestamp for analysis over time + stats.add("timestamp", LLDate::now()); + #if LL_WINDOWS MEMORYSTATUSEX state; state.dwLength = sizeof(state); GlobalMemoryStatusEx(&state); - s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl; - s << "Total Physical KB: " << (U32)(state.ullTotalPhys/1024) << std::endl; - s << "Avail Physical KB: " << (U32)(state.ullAvailPhys/1024) << std::endl; - s << "Total page KB: " << (U32)(state.ullTotalPageFile/1024) << std::endl; - s << "Avail page KB: " << (U32)(state.ullAvailPageFile/1024) << std::endl; - s << "Total Virtual KB: " << (U32)(state.ullTotalVirtual/1024) << std::endl; - s << "Avail Virtual KB: " << (U32)(state.ullAvailVirtual/1024) << std::endl; + stats.add("Percent Memory use", state.dwMemoryLoad); + stats.add("Total Physical KB", state.ullTotalPhys/1024); + stats.add("Avail Physical KB", state.ullAvailPhys/1024); + stats.add("Total page KB", state.ullTotalPageFile/1024); + stats.add("Avail page KB", state.ullAvailPageFile/1024); + stats.add("Total Virtual KB", state.ullTotalVirtual/1024); + stats.add("Avail Virtual KB", state.ullAvailVirtual/1024); + + PERFORMANCE_INFORMATION perf; + perf.cb = sizeof(perf); + GetPerformanceInfo(&perf, sizeof(perf)); + + SIZE_T pagekb(perf.PageSize/1024); + stats.add("CommitTotal KB", perf.CommitTotal * pagekb); + stats.add("CommitLimit KB", perf.CommitLimit * pagekb); + stats.add("CommitPeak KB", perf.CommitPeak * pagekb); + stats.add("PhysicalTotal KB", perf.PhysicalTotal * pagekb); + stats.add("PhysicalAvail KB", perf.PhysicalAvailable * pagekb); + stats.add("SystemCache KB", perf.SystemCache * pagekb); + stats.add("KernelTotal KB", perf.KernelTotal * pagekb); + stats.add("KernelPaged KB", perf.KernelPaged * pagekb); + stats.add("KernelNonpaged KB", perf.KernelNonpaged * pagekb); + stats.add("PageSize KB", pagekb); + stats.add("HandleCount", perf.HandleCount); + stats.add("ProcessCount", perf.ProcessCount); + stats.add("ThreadCount", perf.ThreadCount); + + PROCESS_MEMORY_COUNTERS_EX pmem; + pmem.cb = sizeof(pmem); + // GetProcessMemoryInfo() is documented to accept either + // PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably + // using the redundant size info to distinguish. But its prototype + // specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a + // classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the + // pointer. + GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem)); + + stats.add("Page Fault Count", pmem.PageFaultCount); + stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/1024); + stats.add("WorkingSetSize KB", pmem.WorkingSetSize/1024); + stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/1024); + stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/1024); + stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/1024); + stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/1024); + stats.add("PagefileUsage KB", pmem.PagefileUsage/1024); + stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/1024); + stats.add("PrivateUsage KB", pmem.PrivateUsage/1024); + #elif LL_DARWIN uint64_t phys = 0; size_t len = sizeof(phys); - if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) + if (sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0) { - s << "Total Physical KB: " << phys/1024 << std::endl; + stats.add("Total Physical KB", phys/1024); } else { - s << "Unable to collect memory information"; + LL_WARNS("LLMemoryInfo") << "Unable to collect hw.memsize memory information" << LL_ENDL; + } + + FILE* pout = popen("vm_stat 2>&1", "r"); + if (! pout) // popen() couldn't run vm_stat + { + // Save errno right away. + int popen_errno(errno); + LL_WARNS("LLMemoryInfo") << "Unable to collect vm_stat memory information: "; + char buffer[256]; + if (0 == strerror_r(popen_errno, buffer, sizeof(buffer))) + { + LL_CONT << buffer; + } + else + { + LL_CONT << "errno " << popen_errno; + } + LL_CONT << LL_ENDL; + } + else // popen() launched vm_stat + { + // Mach Virtual Memory Statistics: (page size of 4096 bytes) + // Pages free: 462078. + // Pages active: 142010. + // Pages inactive: 220007. + // Pages wired down: 159552. + // "Translation faults": 220825184. + // Pages copy-on-write: 2104153. + // Pages zero filled: 167034876. + // Pages reactivated: 65153. + // Pageins: 2097212. + // Pageouts: 41759. + // Object cache: 841598 hits of 7629869 lookups (11% hit rate) + + // Intentionally don't pass the boost::no_except flag. These + // boost::regex objects are constructed with string literals, so they + // should be valid every time. If they become invalid, we WANT an + // exception, hopefully even before the dev checks in. + boost::regex pagesize_rx("\\(page size of ([0-9]+) bytes\\)"); + boost::regex stat_rx("(.+): +([0-9]+)\\."); + boost::regex cache_rx("Object cache: ([0-9]+) hits of ([0-9]+) lookups " + "\\(([0-9]+)% hit rate\\)"); + boost::cmatch matched; + LLSD::Integer pagesizekb(4096/1024); + + // Here 'pout' is vm_stat's stdout. Search it for relevant data. + char line[100]; + line[sizeof(line)-1] = '\0'; + while (fgets(line, sizeof(line)-1, pout)) + { + size_t linelen(strlen(line)); + // Truncate any trailing newline + if (line[linelen - 1] == '\n') + { + line[--linelen] = '\0'; + } + LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL; + if (regex_search_no_exc(line, matched, pagesize_rx)) + { + // "Mach Virtual Memory Statistics: (page size of 4096 bytes)" + std::string pagesize_str(matched[1].first, matched[1].second); + try + { + // Reasonable to assume that pagesize will always be a + // multiple of 1Kb? + pagesizekb = boost::lexical_cast<LLSD::Integer>(pagesize_str)/1024; + } + catch (const boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << pagesize_str + << "' in vm_stat line: " << line << LL_ENDL; + continue; + } + stats.add("page size", pagesizekb); + } + else if (regex_match_no_exc(line, matched, stat_rx)) + { + // e.g. "Pages free: 462078." + // Strip double-quotes off certain statistic names + const char *key_begin(matched[1].first), *key_end(matched[1].second); + if (key_begin[0] == '"' && key_end[-1] == '"') + { + ++key_begin; + --key_end; + } + LLSD::String key(key_begin, key_end); + LLSD::String value_str(matched[2].first, matched[2].second); + LLSD::Integer value(0); + try + { + value = boost::lexical_cast<LLSD::Integer>(value_str); + } + catch (const boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str + << "' in vm_stat line: " << line << LL_ENDL; + continue; + } + // Store this statistic. + stats.add(key, value); + // Is this in units of pages? If so, convert to Kb. + static const LLSD::String pages("Pages "); + if (key.substr(0, pages.length()) == pages) + { + // Synthesize a new key with kb in place of Pages + LLSD::String kbkey("kb "); + kbkey.append(key.substr(pages.length())); + stats.add(kbkey, value * pagesizekb); + } + } + else if (regex_match_no_exc(line, matched, cache_rx)) + { + // e.g. "Object cache: 841598 hits of 7629869 lookups (11% hit rate)" + static const char* cache_keys[] = { "cache hits", "cache lookups", "cache hit%" }; + std::vector<LLSD::Integer> cache_values; + for (size_t i = 0; i < (sizeof(cache_keys)/sizeof(cache_keys[0])); ++i) + { + LLSD::String value_str(matched[i+1].first, matched[i+1].second); + LLSD::Integer value(0); + try + { + value = boost::lexical_cast<LLSD::Integer>(value_str); + } + catch (boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str + << "' in vm_stat line: " << line << LL_ENDL; + continue; + } + stats.add(cache_keys[i], value); + } + } + else + { + LL_WARNS("LLMemoryInfo") << "unrecognized vm_stat line: " << line << LL_ENDL; + } + } + int status(pclose(pout)); + if (status == -1) // pclose() couldn't retrieve rc + { + // Save errno right away. + int pclose_errno(errno); + // The ECHILD error happens so frequently that unless filtered, + // the warning below spams the log file. This is too bad, because + // sometimes the logic above fails to produce any output derived + // from vm_stat, but we've been unable to observe any specific + // error indicating the problem. + if (pclose_errno != ECHILD) + { + LL_WARNS("LLMemoryInfo") << "Unable to obtain vm_stat termination code: "; + char buffer[256]; + if (0 == strerror_r(pclose_errno, buffer, sizeof(buffer))) + { + LL_CONT << buffer; + } + else + { + LL_CONT << "errno " << pclose_errno; + } + LL_CONT << LL_ENDL; + } + } + else // pclose() retrieved rc; analyze + { + if (WIFEXITED(status)) + { + int rc(WEXITSTATUS(status)); + if (rc != 0) + { + LL_WARNS("LLMemoryInfo") << "vm_stat terminated with rc " << rc << LL_ENDL; + } + } + else if (WIFSIGNALED(status)) + { + LL_WARNS("LLMemoryInfo") << "vm_stat terminated by signal " << WTERMSIG(status) + << LL_ENDL; + } + } } + #elif LL_SOLARIS - U64 phys = 0; + U64 phys = 0; - phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); + phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024); - s << "Total Physical KB: " << phys << std::endl; -#else - // *NOTE: This works on linux. What will it do on other systems? - LLFILE* meminfo = LLFile::fopen(MEMINFO_FILE,"rb"); - if(meminfo) + stats.add("Total Physical KB", phys); + +#elif LL_LINUX + std::ifstream meminfo(MEMINFO_FILE); + if (meminfo.is_open()) { - char line[MAX_STRING]; /* Flawfinder: ignore */ - memset(line, 0, MAX_STRING); - while(fgets(line, MAX_STRING, meminfo)) + // MemTotal: 4108424 kB + // MemFree: 1244064 kB + // Buffers: 85164 kB + // Cached: 1990264 kB + // SwapCached: 0 kB + // Active: 1176648 kB + // Inactive: 1427532 kB + // ... + // VmallocTotal: 122880 kB + // VmallocUsed: 65252 kB + // VmallocChunk: 52356 kB + // HardwareCorrupted: 0 kB + // HugePages_Total: 0 + // HugePages_Free: 0 + // HugePages_Rsvd: 0 + // HugePages_Surp: 0 + // Hugepagesize: 2048 kB + // DirectMap4k: 434168 kB + // DirectMap2M: 477184 kB + + // Intentionally don't pass the boost::no_except flag. This + // boost::regex object is constructed with a string literal, so it + // should be valid every time. If it becomes invalid, we WANT an + // exception, hopefully even before the dev checks in. + boost::regex stat_rx("(.+): +([0-9]+)( kB)?"); + boost::smatch matched; + + std::string line; + while (std::getline(meminfo, line)) { - line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/ - s << line; + LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL; + if (regex_match_no_exc(line, matched, stat_rx)) + { + // e.g. "MemTotal: 4108424 kB" + LLSD::String key(matched[1].first, matched[1].second); + LLSD::String value_str(matched[2].first, matched[2].second); + LLSD::Integer value(0); + try + { + value = boost::lexical_cast<LLSD::Integer>(value_str); + } + catch (const boost::bad_lexical_cast&) + { + LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str + << "' in " << MEMINFO_FILE << " line: " + << line << LL_ENDL; + continue; + } + // Store this statistic. + stats.add(key, value); + } + else + { + LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: " + << line << LL_ENDL; + } } - fclose(meminfo); } else { - s << "Unable to collect memory information"; + LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL; } + +#else + LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL; + #endif + + return stats.get(); } std::ostream& operator<<(std::ostream& s, const LLOSInfo& info) @@ -778,6 +1281,143 @@ std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info) return s; } +class FrameWatcher +{ +public: + FrameWatcher(): + // Hooking onto the "mainloop" event pump gets us one call per frame. + mConnection(LLEventPumps::instance() + .obtain("mainloop") + .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))), + // Initializing mSampleStart to an invalid timestamp alerts us to skip + // trying to compute framerate on the first call. + mSampleStart(-1), + // Initializing mSampleEnd to 0 ensures that we treat the first call + // as the completion of a sample window. + mSampleEnd(0), + mFrames(0), + // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need + // the number of integer MEM_INFO_THROTTLE sample slots that will fit + // in MEM_INFO_WINDOW. Round up. + mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)), + // Initializing to F32_MAX means that the first real frame will become + // the slowest ever, which sounds like a good idea. + mSlowest(F32_MAX) + {} + + bool tick(const LLSD&) + { + F32 timestamp(mTimer.getElapsedTimeF32()); + + // Count this frame in the interval just completed. + ++mFrames; + + // Have we finished a sample window yet? + if (timestamp < mSampleEnd) + { + // no, just keep waiting + return false; + } + + // Set up for next sample window. Capture values for previous frame in + // local variables and reset data members. + U32 frames(mFrames); + F32 sampleStart(mSampleStart); + // No frames yet in next window + mFrames = 0; + // which starts right now + mSampleStart = timestamp; + // and ends MEM_INFO_THROTTLE seconds in the future + mSampleEnd = mSampleStart + MEM_INFO_THROTTLE; + + // On the very first call, that's all we can do, no framerate + // computation is possible. + if (sampleStart < 0) + { + return false; + } + + // How long did this actually take? As framerate slows, the duration + // of the frame we just finished could push us WELL beyond our desired + // sample window size. + F32 elapsed(timestamp - sampleStart); + F32 framerate(frames/elapsed); + + // Remember previous slowest framerate because we're just about to + // update it. + F32 slowest(mSlowest); + // Remember previous number of samples. + boost::circular_buffer<F32>::size_type prevSize(mSamples.size()); + + // Capture new framerate in our samples buffer. Once the buffer is + // full (after MEM_INFO_WINDOW seconds), this will displace the oldest + // sample. ("So they all rolled over, and one fell out...") + mSamples.push_back(framerate); + + // Calculate the new minimum framerate. I know of no way to update a + // rolling minimum without ever rescanning the buffer. But since there + // are only a few tens of items in this buffer, rescanning it is + // probably cheaper (and certainly easier to reason about) than + // attempting to optimize away some of the scans. + mSlowest = framerate; // pick an arbitrary entry to start + for (boost::circular_buffer<F32>::const_iterator si(mSamples.begin()), send(mSamples.end()); + si != send; ++si) + { + if (*si < mSlowest) + { + mSlowest = *si; + } + } + + // We're especially interested in memory as framerate drops. Only log + // when framerate drops below the slowest framerate we remember. + // (Should always be true for the end of the very first sample + // window.) + if (framerate >= slowest) + { + return false; + } + // Congratulations, we've hit a new low. :-P + + LL_INFOS("FrameWatcher") << ' '; + if (! prevSize) + { + LL_CONT << "initial framerate "; + } + else + { + LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE) + << " seconds "; + } + LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n' + << LLMemoryInfo() << LL_ENDL; + + return false; + } + +private: + // Storing the connection in an LLTempBoundListener ensures it will be + // disconnected when we're destroyed. + LLTempBoundListener mConnection; + // Track elapsed time + LLTimer mTimer; + // Some of what you see here is in fact redundant with functionality you + // can get from LLTimer. Unfortunately the LLTimer API is missing the + // feature we need: has at least the stated interval elapsed, and if so, + // exactly how long has passed? So we have to do it by hand, sigh. + // Time at start, end of sample window + F32 mSampleStart, mSampleEnd; + // Frames this sample window + U32 mFrames; + // Sliding window of framerate samples + boost::circular_buffer<F32> mSamples; + // Slowest framerate in mSamples + F32 mSlowest; +}; + +// Need an instance of FrameWatcher before it does any good +static FrameWatcher sFrameWatcher; + BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile) { std::string tmpfile; diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 41a4f25000..739e795d3a 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -36,6 +36,7 @@ // llinfos << info << llendl; // +#include "llsd.h" #include <iosfwd> #include <string> @@ -117,6 +118,27 @@ public: //get the available memory infomation in KiloBytes. static void getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb); + + // Retrieve a map of memory statistics. The keys of the map are platform- + // dependent. The values are in kilobytes to try to avoid integer overflow. + LLSD getStatsMap() const; + + // Re-fetch memory data (as reported by stream() and getStatsMap()) from the + // system. Normally this is fetched at construction time. Return (*this) + // to permit usage of the form: + // @code + // LLMemoryInfo info; + // ... + // info.refresh().getStatsMap(); + // @endcode + LLMemoryInfo& refresh(); + +private: + // set mStatsMap + static LLSD loadStatsMap(); + + // Memory stats for getStatsMap(). + LLSD mStatsMap; }; diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 3dcaca4f16..0018b8e844 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -29,7 +29,7 @@ const S32 LL_VERSION_MAJOR = 2; const S32 LL_VERSION_MINOR = 8; -const S32 LL_VERSION_PATCH = 0; +const S32 LL_VERSION_PATCH = 1; const S32 LL_VERSION_BUILD = 0; const char * const LL_CHANNEL = "Second Life Developer"; diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 68e45f36e4..93f3c910bd 100644 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -31,10 +31,12 @@ #include "llcrashlogger.h" #include "linden_common.h" #include "llstring.h" -#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME +#include "indra_constants.h" // CRASH_BEHAVIOR_... #include "llerror.h" +#include "llerrorcontrol.h" #include "lltimer.h" #include "lldir.h" +#include "llfile.h" #include "llsdserialize.h" #include "lliopipe.h" #include "llpumpio.h" @@ -54,7 +56,7 @@ public: virtual void error(U32 status, const std::string& reason) { - gBreak = true; + gBreak = true; } virtual void result(const LLSD& content) @@ -64,21 +66,8 @@ public: } }; -bool LLCrashLoggerText::mainLoop() -{ - std::cout << "Entering main loop" << std::endl; - sendCrashLogs(); - return true; -} - -void LLCrashLoggerText::updateApplication(const std::string& message) -{ - LLCrashLogger::updateApplication(message); - std::cout << message << std::endl; -} - LLCrashLogger::LLCrashLogger() : - mCrashBehavior(CRASH_BEHAVIOR_ASK), + mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND), mCrashInPreviousExec(false), mCrashSettings("CrashSettings"), mSentCrashLogs(false), @@ -281,26 +270,48 @@ LLSD LLCrashLogger::constructPostData() return mCrashInfo; } +const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml"; + S32 LLCrashLogger::loadCrashBehaviorSetting() { + // First check user_settings (in the user's home dir) std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); + if (! mCrashSettings.loadFromFile(filename)) + { + // Next check app_settings (in the SL program dir) + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, CRASH_SETTINGS_FILE); + mCrashSettings.loadFromFile(filename); + } - mCrashSettings.loadFromFile(filename); - - S32 value = mCrashSettings.getS32(CRASH_BEHAVIOR_SETTING); - - if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK; + // If we didn't load any files above, this will return the default + S32 value = mCrashSettings.getS32("CrashSubmitBehavior"); - return value; + // Whatever value we got, make sure it's valid + switch (value) + { + case CRASH_BEHAVIOR_NEVER_SEND: + return CRASH_BEHAVIOR_NEVER_SEND; + case CRASH_BEHAVIOR_ALWAYS_SEND: + return CRASH_BEHAVIOR_ALWAYS_SEND; + } + + return CRASH_BEHAVIOR_ASK; } bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior) { - if (crash_behavior != CRASH_BEHAVIOR_ASK && crash_behavior != CRASH_BEHAVIOR_ALWAYS_SEND) return false; + switch (crash_behavior) + { + case CRASH_BEHAVIOR_ASK: + case CRASH_BEHAVIOR_NEVER_SEND: + case CRASH_BEHAVIOR_ALWAYS_SEND: + break; + default: + return false; + } - mCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior); + mCrashSettings.setS32("CrashSubmitBehavior", crash_behavior); std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - mCrashSettings.saveToFile(filename, FALSE); return true; @@ -309,14 +320,13 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior) bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout) { gBreak = false; - std::string status_message; for(int i = 0; i < retries; ++i) { - status_message = llformat("%s, try %d...", msg.c_str(), i+1); + updateApplication(llformat("%s, try %d...", msg.c_str(), i+1)); LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout); while(!gBreak) { - updateApplication(status_message); + updateApplication(); // No new message, just pump the IO } if(gSent) { @@ -336,7 +346,7 @@ bool LLCrashLogger::sendCrashLogs() updateApplication("Sending reports..."); std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - "SecondLifeCrashReport"); + "SecondLifeCrashReport"); std::string report_file = dump_path + ".log"; std::ofstream out_file(report_file.c_str()); @@ -365,6 +375,7 @@ void LLCrashLogger::updateApplication(const std::string& message) { gServicePump->pump(); gServicePump->callback(); + if (!message.empty()) llinfos << message << llendl; } bool LLCrashLogger::init() @@ -374,14 +385,27 @@ bool LLCrashLogger::init() // We assume that all the logs we're looking for reside on the current drive gDirUtilp->initAppDirs("SecondLife"); + LLError::initForApplication(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + // Default to the product name "Second Life" (this is overridden by the -name argument) mProductName = "Second Life"; + + // Rename current log file to ".old" + std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old"); + std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log"); + LLFile::rename(log_file.c_str(), old_log_file.c_str()); + + // Set the log file to crashreport.log + LLError::logToFile(log_file); - mCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes " - "(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)"); + mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND, + "Controls behavior when viewer crashes " + "(0 = ask before sending crash report, " + "1 = always send crash report, " + "2 = never send crash report)"); - llinfos << "Loading crash behavior setting" << llendl; - mCrashBehavior = loadCrashBehaviorSetting(); + // llinfos << "Loading crash behavior setting" << llendl; + // mCrashBehavior = loadCrashBehaviorSetting(); // If user doesn't want to send, bail out if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND) @@ -394,10 +418,11 @@ bool LLCrashLogger::init() gServicePump->prime(gAPRPoolp); LLHTTPClient::setPump(*gServicePump); - //If we've opened the crash logger, assume we can delete the marker file if it exists + //If we've opened the crash logger, assume we can delete the marker file if it exists if( gDirUtilp ) { - std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker"); + std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.exec_marker"); LLAPRFile::remove( marker_file ); } diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h index a5daa74247..5d0cb5931c 100644 --- a/indra/llcrashlogger/llcrashlogger.h +++ b/indra/llcrashlogger/llcrashlogger.h @@ -66,15 +66,4 @@ protected: bool mSentCrashLogs; }; -class LLCrashLoggerText : public LLCrashLogger -{ -public: - LLCrashLoggerText(void) {} - ~LLCrashLoggerText(void) {} - - virtual bool mainLoop(); - virtual void updateApplication(const std::string& message = LLStringUtil::null); -}; - - #endif //LLCRASHLOGGER_H diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 9dadad7dd3..cd100cdf9f 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -12,6 +12,8 @@ include_directories( set(llmath_SOURCE_FILES llbbox.cpp llbboxlocal.cpp + llcalc.cpp + llcalcparser.cpp llcamera.cpp llcoordframe.cpp llline.cpp @@ -46,6 +48,8 @@ set(llmath_HEADER_FILES coordframe.h llbbox.h llbboxlocal.h + llcalc.h + llcalcparser.h llcamera.h llcoord.h llcoordframe.h diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp new file mode 100644 index 0000000000..597d0815fb --- /dev/null +++ b/indra/llmath/llcalc.cpp @@ -0,0 +1,145 @@ +/* + * LLCalc.cpp + * SecondLife + * + * Created by Aimee Walton on 28/09/2008. + * Copyright 2008 Aimee Walton. + * + */ + +#include "linden_common.h" + +#include "llcalc.h" + +#include "llcalcparser.h" +#include "llmath.h" + + +// Variable names for use in the build floater +const char* LLCalc::X_POS = "PX"; +const char* LLCalc::Y_POS = "PY"; +const char* LLCalc::Z_POS = "PZ"; +const char* LLCalc::X_SCALE = "SX"; +const char* LLCalc::Y_SCALE = "SY"; +const char* LLCalc::Z_SCALE = "SZ"; +const char* LLCalc::X_ROT = "RX"; +const char* LLCalc::Y_ROT = "RY"; +const char* LLCalc::Z_ROT = "RZ"; +const char* LLCalc::HOLLOW = "HLW"; +const char* LLCalc::CUT_BEGIN = "CB"; +const char* LLCalc::CUT_END = "CE"; +const char* LLCalc::PATH_BEGIN = "PB"; +const char* LLCalc::PATH_END = "PE"; +const char* LLCalc::TWIST_BEGIN = "TB"; +const char* LLCalc::TWIST_END = "TE"; +const char* LLCalc::X_SHEAR = "SHX"; +const char* LLCalc::Y_SHEAR = "SHY"; +const char* LLCalc::X_TAPER = "TPX"; +const char* LLCalc::Y_TAPER = "TPY"; +const char* LLCalc::RADIUS_OFFSET = "ROF"; +const char* LLCalc::REVOLUTIONS = "REV"; +const char* LLCalc::SKEW = "SKW"; +const char* LLCalc::X_HOLE = "HLX"; +const char* LLCalc::Y_HOLE = "HLY"; +const char* LLCalc::TEX_U_SCALE = "TSU"; +const char* LLCalc::TEX_V_SCALE = "TSV"; +const char* LLCalc::TEX_U_OFFSET = "TOU"; +const char* LLCalc::TEX_V_OFFSET = "TOV"; +const char* LLCalc::TEX_ROTATION = "TROT"; +const char* LLCalc::TEX_TRANSPARENCY = "TRNS"; +const char* LLCalc::TEX_GLOW = "GLOW"; + + +LLCalc* LLCalc::sInstance = NULL; + +LLCalc::LLCalc() : mLastErrorPos(0) +{ + // Init table of constants + mConstants["PI"] = F_PI; + mConstants["TWO_PI"] = F_TWO_PI; + mConstants["PI_BY_TWO"] = F_PI_BY_TWO; + mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI; + mConstants["SQRT2"] = F_SQRT2; + mConstants["SQRT3"] = F_SQRT3; + mConstants["DEG_TO_RAD"] = DEG_TO_RAD; + mConstants["RAD_TO_DEG"] = RAD_TO_DEG; + mConstants["GRAVITY"] = GRAVITY; +} + +LLCalc::~LLCalc() +{ +} + +//static +void LLCalc::cleanUp() +{ + delete sInstance; + sInstance = NULL; +} + +//static +LLCalc* LLCalc::getInstance() +{ + if (!sInstance) sInstance = new LLCalc(); + return sInstance; +} + +void LLCalc::setVar(const std::string& name, const F32& value) +{ + mVariables[name] = value; +} + +void LLCalc::clearVar(const std::string& name) +{ + mVariables.erase(name); +} + +void LLCalc::clearAllVariables() +{ + mVariables.clear(); +} + +/* +void LLCalc::updateVariables(LLSD& vars) +{ + LLSD::map_iterator cIt = vars.beginMap(); + for(; cIt != vars.endMap(); cIt++) + { + setVar(cIt->first, (F32)(LLSD::Real)cIt->second); + } +} +*/ + +bool LLCalc::evalString(const std::string& expression, F32& result) +{ + std::string expr_upper = expression; + LLStringUtil::toUpper(expr_upper); + + LLCalcParser calc(result, &mConstants, &mVariables); + + mLastErrorPos = 0; + std::string::iterator start = expr_upper.begin(); + parse_info<std::string::iterator> info; + + try + { + info = parse(start, expr_upper.end(), calc, space_p); + lldebugs << "Math expression: " << expression << " = " << result << llendl; + } + catch(parser_error<std::string, std::string::iterator> &e) + { + mLastErrorPos = e.where - expr_upper.begin(); + + llinfos << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << llendl; + return false; + } + + if (!info.full) + { + mLastErrorPos = info.stop - expr_upper.begin(); + llinfos << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << llendl; + return false; + } + + return true; +} diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h new file mode 100644 index 0000000000..cc31950cb6 --- /dev/null +++ b/indra/llmath/llcalc.h @@ -0,0 +1,83 @@ +/* + * LLCalc.h + * SecondLife + * + * Created by Aimee Walton on 28/09/2008. + * Copyright 2008 Aimee Walton. + * + */ + +#ifndef LL_CALC_H +#define LL_CALC_H + +#include <map> +#include <string> + +class LLCalc +{ +public: + LLCalc(); + ~LLCalc(); + + // Variable name constants + static const char* X_POS; + static const char* Y_POS; + static const char* Z_POS; + static const char* X_SCALE; + static const char* Y_SCALE; + static const char* Z_SCALE; + static const char* X_ROT; + static const char* Y_ROT; + static const char* Z_ROT; + static const char* HOLLOW; + static const char* CUT_BEGIN; + static const char* CUT_END; + static const char* PATH_BEGIN; + static const char* PATH_END; + static const char* TWIST_BEGIN; + static const char* TWIST_END; + static const char* X_SHEAR; + static const char* Y_SHEAR; + static const char* X_TAPER; + static const char* Y_TAPER; + static const char* RADIUS_OFFSET; + static const char* REVOLUTIONS; + static const char* SKEW; + static const char* X_HOLE; + static const char* Y_HOLE; + static const char* TEX_U_SCALE; + static const char* TEX_V_SCALE; + static const char* TEX_U_OFFSET; + static const char* TEX_V_OFFSET; + static const char* TEX_ROTATION; + static const char* TEX_TRANSPARENCY; + static const char* TEX_GLOW; + + void setVar(const std::string& name, const F32& value); + void clearVar(const std::string& name); + void clearAllVariables(); +// void updateVariables(LLSD& vars); + + bool evalString(const std::string& expression, F32& result); + std::string::size_type getLastErrorPos() { return mLastErrorPos; } + + static LLCalc* getInstance(); + static void cleanUp(); + + typedef std::map<std::string, F32> calc_map_t; + +private: + std::string::size_type mLastErrorPos; + + calc_map_t mConstants; + calc_map_t mVariables; + + // *TODO: Add support for storing user defined variables, and stored functions. + // Will need UI work, and a means to save them between sessions. +// calc_map_t mUserVariables; + + // "There shall be only one" + static LLCalc* sInstance; +}; + +#endif // LL_CALC_H diff --git a/indra/llmath/llcalcparser.cpp b/indra/llmath/llcalcparser.cpp new file mode 100644 index 0000000000..fd55376fa9 --- /dev/null +++ b/indra/llmath/llcalcparser.cpp @@ -0,0 +1,46 @@ +/* + * LLCalcParser.cpp + * SecondLife + * + * Created by Aimee Walton on 28/09/2008. + * Copyright 2008 Aimee Walton. + * + */ + +#include "linden_common.h" + +#include "llcalcparser.h" +using namespace boost::spirit::classic; + +F32 LLCalcParser::lookup(const std::string::iterator& start, const std::string::iterator& end) const +{ + LLCalc::calc_map_t::iterator iter; + + std::string name(start, end); + + if (mConstants) + { + iter = mConstants->find(name); + if (iter != mConstants->end()) + { + return (*iter).second; + } + } + else + { + // This should never happen! + throw_(end, std::string("Missing constants table")); + } + + if (mVariables) + { + iter = mVariables->find(name); + if (iter != mVariables->end()) + { + return (*iter).second; + } + } + + throw_(end, std::string("Unknown symbol " + name)); + return 0.f; +} diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h new file mode 100644 index 0000000000..600e173661 --- /dev/null +++ b/indra/llmath/llcalcparser.h @@ -0,0 +1,174 @@ +/* + * LLCalcParser.h + * SecondLife + * + * Created by Aimee Walton on 28/09/2008. + * Copyright 2008 Aimee Walton. + * + */ + +#ifndef LL_CALCPARSER_H +#define LL_CALCPARSER_H + +#include <boost/spirit/include/classic_attribute.hpp> +#include <boost/spirit/include/classic_core.hpp> +#include <boost/spirit/include/classic_error_handling.hpp> +#include <boost/spirit/include/classic_position_iterator.hpp> +#include <boost/spirit/include/phoenix1_binders.hpp> +#include <boost/spirit/include/classic_symbols.hpp> +using namespace boost::spirit::classic; + +#include "llcalc.h" +#include "llmath.h" + +struct LLCalcParser : grammar<LLCalcParser> +{ + LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) : + mResult(result), mConstants(constants), mVariables(vars) {}; + + struct value_closure : closure<value_closure, F32> + { + member1 value; + }; + + template <typename ScannerT> + struct definition + { + // Rule declarations + rule<ScannerT> statement, identifier; + rule<ScannerT, value_closure::context_t> expression, term, + power, + unary_expr, + factor, + unary_func, + binary_func, + group; + + // start() should return the starting symbol + rule<ScannerT> const& start() const { return statement; } + + definition(LLCalcParser const& self) + { + using namespace phoenix; + + assertion<std::string> assert_domain("Domain error"); +// assertion<std::string> assert_symbol("Unknown symbol"); + assertion<std::string> assert_syntax("Syntax error"); + + identifier = + lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')] + ; + + group = + '(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')')) + ; + + unary_func = + ((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sin)(self,arg1)]) | + (str_p("COS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_cos)(self,arg1)]) | + (str_p("TAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_tan)(self,arg1)]) | + (str_p("ASIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_asin)(self,arg1)]) | + (str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_acos)(self,arg1)]) | + (str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_atan)(self,arg1)]) | + (str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sqrt)(self,arg1)]) | + (str_p("LOG") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_log)(self,arg1)]) | + (str_p("EXP") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_exp)(self,arg1)]) | + (str_p("ABS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_fabs)(self,arg1)]) | + (str_p("FLR") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_floor)(self,arg1)]) | + (str_p("CEIL") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_ceil)(self,arg1)]) + ) >> assert_syntax(ch_p(')')) + ; + + binary_func = + ((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >> + expression[binary_func.value = bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) | + (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >> + expression[binary_func.value = bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) | + (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >> + expression[binary_func.value = bind(&LLCalcParser::_max)(self, binary_func.value, arg1)]) + ) >> assert_syntax(ch_p(')')) + ; + + // *TODO: Localisation of the decimal point? + // Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate + // for the current locale. However to do that here could clash with using + // the comma as a separator when passing arguments to functions. + factor = + (ureal_p[factor.value = arg1] | + group[factor.value = arg1] | + unary_func[factor.value = arg1] | + binary_func[factor.value = arg1] | + // Lookup throws an Unknown Symbol error if it is unknown, while this works fine, + // would be "neater" to handle symbol lookup from here with an assertive parser. +// constants_p[factor.value = arg1]| + identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)] + ) >> + // Detect and throw math errors. + assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value))) + ; + + unary_expr = + !ch_p('+') >> factor[unary_expr.value = arg1] | + '-' >> factor[unary_expr.value = -arg1] + ; + + power = + unary_expr[power.value = arg1] >> + *('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)])) + ; + + term = + power[term.value = arg1] >> + *(('*' >> assert_syntax(power[term.value *= arg1])) | + ('/' >> assert_syntax(power[term.value /= arg1])) | + ('%' >> assert_syntax(power[term.value = bind(&fmodf)(term.value, arg1)])) + ) + ; + + expression = + assert_syntax(term[expression.value = arg1]) >> + *(('+' >> assert_syntax(term[expression.value += arg1])) | + ('-' >> assert_syntax(term[expression.value -= arg1])) + ) + ; + + statement = + !ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p) + ; + } + }; + +private: + // Member functions for semantic actions + F32 lookup(const std::string::iterator&, const std::string::iterator&) const; + F32 _min(const F32& a, const F32& b) const { return llmin(a, b); } + F32 _max(const F32& a, const F32& b) const { return llmax(a, b); } + + bool checkNaN(const F32& a) const { return !llisnan(a); } + + //FIX* non ambigious function fix making SIN() work for calc -Cryogenic Blitz + F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); } + F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); } + F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); } + F32 _asin(const F32& a) const { return asin(a * RAD_TO_DEG); } + F32 _acos(const F32& a) const { return acos(a * RAD_TO_DEG); } + F32 _atan(const F32& a) const { return atan(a * RAD_TO_DEG); } + F32 _sqrt(const F32& a) const { return sqrt(a); } + F32 _log(const F32& a) const { return log(a); } + F32 _exp(const F32& a) const { return exp(a); } + F32 _fabs(const F32& a) const { return fabs(a); } + F32 _floor(const F32& a) const { return llfloor(a); } + F32 _ceil(const F32& a) const { return llceil(a); } + + F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); } + + + + LLCalc::calc_map_t* mConstants; + LLCalc::calc_map_t* mVariables; +// LLCalc::calc_map_t* mUserVariables; + + F32& mResult; +}; + +#endif // LL_CALCPARSER_H diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 123997e5e9..06fbc0f234 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -37,6 +37,7 @@ #include "llgl.h" #include "lltimer.h" +#include "llcalc.h" //#include "llclipboard.h" #include "llcontrol.h" #include "llbutton.h" @@ -133,6 +134,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) mIgnoreTab( p.ignore_tab ), mDrawAsterixes( p.is_password ), mSelectAllonFocusReceived( p.select_on_focus ), + mSelectAllonCommit( TRUE ), mPassDelete(FALSE), mReadOnly(FALSE), mBgImage( p.background_image ), @@ -230,7 +232,10 @@ void LLLineEditor::onCommit() setControlValue(getValue()); LLUICtrl::onCommit(); - selectAll(); + + // Selection on commit needs to be turned off when evaluating maths + // expressions, to allow indication of the error position + if (mSelectAllonCommit) selectAll(); } // Returns TRUE if user changed value at all @@ -2072,6 +2077,32 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str) return success; } +BOOL LLLineEditor::evaluateFloat() +{ + bool success; + F32 result = 0.f; + std::string expr = getText(); + LLStringUtil::toUpper(expr); + + success = LLCalc::getInstance()->evalString(expr, result); + + if (!success) + { + // Move the cursor to near the error on failure + setCursor(LLCalc::getInstance()->getLastErrorPos()); + // *TODO: Translated error message indicating the type of error? Select error text? + } + else + { + // Replace the expression with the result + std::string result_str = llformat("%f",result); + setText(result_str); + selectAll(); + } + + return success; +} + void LLLineEditor::onMouseCaptureLost() { endSelection(); diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 4b77708590..583bde360a 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -221,6 +221,7 @@ public: void deleteSelection(); void setSelectAllonFocusReceived(BOOL b); + void setSelectAllonCommit(BOOL b) { mSelectAllonCommit = b; } typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t; void setKeystrokeCallback(callback_t callback, void* user_data); @@ -241,6 +242,7 @@ public: static BOOL postvalidateFloat(const std::string &str); bool prevalidateInput(const LLWString& wstr); + BOOL evaluateFloat(); // line history support: void setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off @@ -340,6 +342,7 @@ protected: BOOL mDrawAsterixes; BOOL mSelectAllonFocusReceived; + BOOL mSelectAllonCommit; BOOL mPassDelete; BOOL mReadOnly; diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 15a7438ec9..934879cdfd 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -44,7 +44,7 @@ #include "llresmgr.h" #include "lluictrlfactory.h" -const U32 MAX_STRING_LENGTH = 32; +const U32 MAX_STRING_LENGTH = 255; static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner"); @@ -124,14 +124,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) params.max_length.bytes(MAX_STRING_LENGTH); params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2))); - if( mPrecision>0 )//should accept float numbers - { - params.prevalidate_callback(&LLTextValidate::validateFloat); - } - else //should accept int numbers - { - params.prevalidate_callback(&LLTextValidate::validateInt); - } + //*NOTE: allow entering of any chars for LLCalc, proper input will be evaluated on commit params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); mEditor = LLUICtrlFactory::create<LLLineEditor> (params); @@ -140,6 +133,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) // than when it doesn't. Instead, if you always have to double click to select all the text, // it's easier to understand //mEditor->setSelectAllonFocusReceived(TRUE); + mEditor->setSelectAllonCommit(FALSE); addChild(mEditor); updateEditor(); @@ -304,9 +298,10 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data ) { BOOL success = FALSE; - std::string text = mEditor->getText(); - if( LLLineEditor::postvalidateFloat( text ) ) + if( mEditor->evaluateFloat() ) { + std::string text = mEditor->getText(); + LLLocale locale(LLLocale::USER_LOCALE); F32 val = (F32) atof(text.c_str()); @@ -327,7 +322,11 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data ) } updateEditor(); - if( !success ) + if( success ) + { + updateEditor(); + } + else { reportInvalidData(); } diff --git a/indra/mac_crash_logger/CrashReporter.nib/objects.xib b/indra/mac_crash_logger/CrashReporter.nib/objects.xib index 634d1c5321..32647391b6 100644 --- a/indra/mac_crash_logger/CrashReporter.nib/objects.xib +++ b/indra/mac_crash_logger/CrashReporter.nib/objects.xib @@ -15,7 +15,7 @@ <string name="bounds">414 390 434 487 </string> </object> <object class="IBCarbonStaticText" id="181"> - <string name="title">Second Life appears to have crashed or frozen the last time it ran. This crash reporter collects information about your computer's hardware configuration, operating system, and some Second Life logs, all of which are used for debugging purposes only. In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help! This report is NOT read by Customer Support. If you have billing or other questions, please go to: http://www.secondlife.com/support/ If you don't wish to send Linden Lab a crash report, press Cancel. </string> + <string name="title">Second Life appears to have crashed or frozen the last time it ran. This crash reporter collects information about your computer's hardware configuration, operating system, and some Second Life logs, all of which are used for debugging purposes only. In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help! This report is NOT read by Customer Support. If you have billing or other questions, please go to: http://www.secondlife.com/support/ If you don't wish to send Linden Lab a crash report, press Don't Send. </string> <string name="bounds">20 20 231 487 </string> </object> <object class="IBCarbonWindow" id="166"> diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp index bec8cce04e..b555e92b96 100644 --- a/indra/mac_crash_logger/llcrashloggermac.cpp +++ b/indra/mac_crash_logger/llcrashloggermac.cpp @@ -29,9 +29,6 @@ #include <Carbon/Carbon.h> #include <iostream> -#include <sstream> - -#include "boost/tokenizer.hpp" #include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME #include "llerror.h" @@ -247,7 +244,7 @@ bool LLCrashLoggerMac::mainLoop() void LLCrashLoggerMac::updateApplication(const std::string& message) { - LLCrashLogger::updateApplication(); + LLCrashLogger::updateApplication(message); } bool LLCrashLoggerMac::cleanup() diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp index 20b491c401..6571b35241 100644 --- a/indra/mac_crash_logger/mac_crash_logger.cpp +++ b/indra/mac_crash_logger/mac_crash_logger.cpp @@ -25,22 +25,23 @@ */ #include "linden_common.h" - #include "llcrashloggermac.h" int main(int argc, char **argv) { - //time(&gLaunchTime); - - llinfos << "Starting Second Life Viewer Crash Reporter" << llendl; + llinfos << "Starting crash reporter." << llendl; LLCrashLoggerMac app; app.parseCommandOptions(argc, argv); - if(!app.init()) + + if (! app.init()) { - return 0; + llwarns << "Unable to initialize application." << llendl; + return 1; } + app.mainLoop(); - + app.cleanup(); + llinfos << "Crash reporter finished normally." << llendl; return 0; } diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml index 89e5949fbe..15434f2b8f 100644 --- a/indra/newview/app_settings/cmd_line.xml +++ b/indra/newview/app_settings/cmd_line.xml @@ -220,8 +220,7 @@ <map> <key>desc</key> <string>Set the detail level. - 0 - low, 1 - medium, 2 - high, 3 - ultra - </string> +0 - low, 1 - medium, 2 - high, 3 - ultra</string> <key>count</key> <integer>1</integer> </map> @@ -229,10 +228,7 @@ <key>setdefault</key> <map> <key>desc</key> - <string> specify the value of a particular - configuration variable which can be - overridden by settings.xml - </string> + <string>specify the value of a particular configuration variable which can be overridden by settings.xml.</string> <key>count</key> <integer>2</integer> <!-- Special case. Mapped to settings procedurally. --> @@ -241,10 +237,7 @@ <key>set</key> <map> <key>desc</key> - <string> specify the value of a particular - configuration variable that - overrides all other settings - </string> + <string>specify the value of a particular configuration variable that overrides all other settings.</string> <key>count</key> <integer>2</integer> <key>compose</key> diff --git a/indra/newview/app_settings/settings_files.xml b/indra/newview/app_settings/settings_files.xml index 079a54f957..bfc09286e3 100644 --- a/indra/newview/app_settings/settings_files.xml +++ b/indra/newview/app_settings/settings_files.xml @@ -20,7 +20,8 @@ file_name="settings.xml" file_name_setting="ClientSettingsFile"/> <file name="CrashSettings" - file_name="settings_crash_behavior"/> + file_name="settings_crash_behavior.xml" + file_name_setting="CrashSettingsFile"/> <file name="Warnings" file_name="ignorable_dialogs.xml" file_name_setting="WarningSettingsFile"/> @@ -61,4 +62,4 @@ file_name="colors.xml" file_name_setting="SkinningSettingsFile"/> </group> -</settings_files>
\ No newline at end of file +</settings_files> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 8954937f69..492cfe7c1b 100644..100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -577,7 +577,10 @@ void LLAgent::setFlying(BOOL fly) // static void LLAgent::toggleFlying() { - LLToolPie::instance().stopClickToWalk(); + if ( gAgent.mAutoPilot ) + { + LLToolPie::instance().stopClickToWalk(); + } BOOL fly = !gAgent.getFlying(); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1d9519d675..92e0513464 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -56,6 +56,7 @@ #include "llallocator.h" #include "llares.h" #include "llcurl.h" +#include "llcalc.h" #include "lltexturestats.h" #include "lltexturestats.h" #include "llviewerwindow.h" @@ -468,18 +469,6 @@ void request_initial_instant_messages() } } -// A settings system callback for CrashSubmitBehavior -bool handleCrashSubmitBehaviorChanged(const LLSD& newvalue) -{ - S32 cb = newvalue.asInteger(); - const S32 NEVER_SUBMIT_REPORT = 2; - if(cb == NEVER_SUBMIT_REPORT) - { - LLAppViewer::instance()->destroyMainloopTimeout(); - } - return true; -} - // Use these strictly for things that are constructed at startup, // or for things that are performance critical. JC static void settings_to_globals() @@ -611,9 +600,6 @@ bool LLAppViewer::sendURLToOtherInstance(const std::string& url) // Static members. // The single viewer app. LLAppViewer* LLAppViewer::sInstance = NULL; - -const std::string LLAppViewer::sGlobalSettingsName = "Global"; - LLTextureCache* LLAppViewer::sTextureCache = NULL; LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; LLTextureFetch* LLAppViewer::sTextureFetch = NULL; @@ -771,16 +757,6 @@ bool LLAppViewer::init() LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL; LL_INFOS("InitInfo") << "libcurl version is: " << LLCurl::getVersionString() << LL_ENDL; - // Get the single value from the crash settings file, if it exists - std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - gCrashSettings.loadFromFile(crash_settings_filename); - if(gSavedSettings.getBOOL("IgnoreAllNotifications")) - { - gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ALWAYS_SEND); - gCrashSettings.saveToFile(crash_settings_filename, FALSE); - } - LL_INFOS("InitInfo") << "Crash settings done." << LL_ENDL ; - ///////////////////////////////////////////////// // OS-specific login dialogs ///////////////////////////////////////////////// @@ -1055,7 +1031,7 @@ bool LLAppViewer::init() //EXT-7013 - On windows for some locale (Japanese) standard //datetime formatting functions didn't support some parameters such as "weekday". //Names for days and months localized in xml are also useful for Polish locale(STORM-107). - std::string language = LLControlGroup::getInstance(sGlobalSettingsName)->getString("Language"); + std::string language = gSavedSettings.getString("Language"); if(language == "ja" || language == "pl") { LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames")); @@ -1543,7 +1519,9 @@ bool LLAppViewer::cleanup() // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted. LLWorldMap::getInstance()->reset(); // release any images - + + LLCalc::cleanUp(); + llinfos << "Global stuff deleted" << llendflush; if (gAudiop) @@ -1706,10 +1684,6 @@ bool LLAppViewer::cleanup() llinfos << "Saved settings" << llendflush; } - std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - // save all settings, even if equals defaults - gCrashSettings.saveToFile(crash_settings_filename, FALSE); - std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings")); gWarningSettings.saveToFile(warnings_settings_filename, TRUE); @@ -1839,7 +1813,6 @@ bool LLAppViewer::cleanup() gSavedSettings.cleanup(); LLUIColorTable::instance().clear(); - gCrashSettings.cleanup(); LLWatchdog::getInstance()->cleanup(); @@ -1982,7 +1955,6 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, llerrs << "Invalid settings location list" << llendl; } - LLControlGroup* global_settings = LLControlGroup::getInstance(sGlobalSettingsName); for(LLInitParam::ParamIterator<SettingsGroup>::const_iterator it = mSettingsLocationList->groups.begin(), end_it = mSettingsLocationList->groups.end(); it != end_it; ++it) @@ -2015,11 +1987,15 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, std::string full_settings_path; if (file_it->file_name_setting.isProvided() - && global_settings->controlExists(file_it->file_name_setting)) + && gSavedSettings.controlExists(file_it->file_name_setting)) { // try to find filename stored in file_name_setting control - full_settings_path = global_settings->getString(file_it->file_name_setting); - if (!gDirUtilp->fileExists(full_settings_path)) + full_settings_path = gSavedSettings.getString(file_it->file_name_setting); + if (full_settings_path.empty()) + { + continue; + } + else if (!gDirUtilp->fileExists(full_settings_path)) { // search in default path full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, full_settings_path); @@ -2165,8 +2141,6 @@ bool LLAppViewer::initConfiguration() gSavedSettings.setS32("WatchdogEnabled", 0); #endif - gCrashSettings.getControl(CRASH_BEHAVIOR_SETTING)->getSignal()->connect(boost::bind(&handleCrashSubmitBehaviorChanged, _2)); - // These are warnings that appear on the first experience of that condition. // They are already set in the settings_default.xml file, but still need to be added to LLFirstUse // for disable/reset ability @@ -2297,15 +2271,33 @@ bool LLAppViewer::initConfiguration() { const std::string& name = *itr; const std::string& value = *(++itr); - LLControlVariable* c = LLControlGroup::getInstance(sGlobalSettingsName)->getControl(name); - if(c) + std::string name_part; + std::string group_part; + LLControlVariable* control = NULL; + + // Name can be further split into ControlGroup.Name, with the default control group being Global + size_t pos = name.find('.'); + if (pos != std::string::npos) + { + group_part = name.substr(0, pos); + name_part = name.substr(pos+1); + llinfos << "Setting " << group_part << "." << name_part << " to " << value << llendl; + LLControlGroup* g = LLControlGroup::getInstance(group_part); + if (g) control = g->getControl(name_part); + } + else + { + llinfos << "Setting Global." << name << " to " << value << llendl; + control = gSavedSettings.getControl(name); + } + + if (control) { - c->setValue(value, false); + control->setValue(value, false); } else { - llwarns << "'--set' specified with unknown setting: '" - << name << "'." << llendl; + llwarns << "Failed --set " << name << ": setting name unknown." << llendl; } } } @@ -2762,7 +2754,8 @@ void LLAppViewer::checkForCrash(void) // Pop up a freeze or crash warning dialog // S32 choice; - if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_ASK) + const S32 cb = gCrashSettings.getS32("CrashSubmitBehavior"); + if(cb == CRASH_BEHAVIOR_ASK) { std::ostringstream msg; msg << LLTrans::getString("MBFrozenCrashed"); @@ -2771,7 +2764,7 @@ void LLAppViewer::checkForCrash(void) alert, OSMB_YESNO); } - else if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_NEVER_SEND) + else if(cb == CRASH_BEHAVIOR_NEVER_SEND) { choice = OSBTN_NO; } @@ -2828,10 +2821,9 @@ bool LLAppViewer::initWindow() LL_INFOS("AppInit") << "gViewerwindow created." << LL_ENDL; // Need to load feature table before cheking to start watchdog. - const S32 NEVER_SUBMIT_REPORT = 2; bool use_watchdog = false; int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); - if(watchdog_enabled_setting == -1) + if (watchdog_enabled_setting == -1) { use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled"); } @@ -2841,8 +2833,7 @@ bool LLAppViewer::initWindow() use_watchdog = bool(watchdog_enabled_setting); } - bool send_reports = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) != NEVER_SUBMIT_REPORT; - if(use_watchdog && send_reports) + if (use_watchdog) { LLWatchdog::getInstance()->init(watchdog_killer_callback); } diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 714e0e6163..48d02dfeaa 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -361,46 +361,35 @@ void LLAppViewerLinux::handleCrashReporting(bool reportFreeze) } else { - const S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING); - - // Always generate the report, have the logger do the asking, and - // don't wait for the logger before exiting (-> total cleanup). - if (CRASH_BEHAVIOR_NEVER_SEND != cb) - { - // launch the actual crash logger - const char* ask_dialog = "-dialog"; - if (CRASH_BEHAVIOR_ASK != cb) - ask_dialog = ""; // omit '-dialog' option - const char * cmdargv[] = - {cmd.c_str(), - ask_dialog, - "-user", - (char*)LLGridManager::getInstance()->getGridLabel().c_str(), - "-name", - LLAppViewer::instance()->getSecondLifeTitle().c_str(), - NULL}; - fflush(NULL); - pid_t pid = fork(); - if (pid == 0) - { // child - execv(cmd.c_str(), (char* const*) cmdargv); /* Flawfinder: ignore */ - llwarns << "execv failure when trying to start " << cmd << llendl; - _exit(1); // avoid atexit() + // launch the actual crash logger + const char * cmdargv[] = + {cmd.c_str(), + "-user", + (char*)LLGridManager::getInstance()->getGridLabel().c_str(), + "-name", + LLAppViewer::instance()->getSecondLifeTitle().c_str(), + NULL}; + fflush(NULL); + pid_t pid = fork(); + if (pid == 0) + { // child + execv(cmd.c_str(), (char* const*) cmdargv); /* Flawfinder: ignore */ + llwarns << "execv failure when trying to start " << cmd << llendl; + _exit(1); // avoid atexit() + } + else + { + if (pid > 0) + { + // DO NOT wait for child proc to die; we want + // the logger to outlive us while we quit to + // free up the screen/keyboard/etc. + ////int childExitStatus; + ////waitpid(pid, &childExitStatus, 0); } else { - if (pid > 0) - { - // DO NOT wait for child proc to die; we want - // the logger to outlive us while we quit to - // free up the screen/keyboard/etc. - ////int childExitStatus; - ////waitpid(pid, &childExitStatus, 0); - } - else - { - llwarns << "fork failure." << llendl; - } + llwarns << "fork failure." << llendl; } } // Sometimes signals don't seem to quit the viewer. Also, we may diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 445bd208ef..f94c843ad9 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -518,11 +518,7 @@ void LLAppViewerWin32::handleCrashReporting(bool reportFreeze) } else { - S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING); - if(cb != CRASH_BEHAVIOR_NEVER_SEND) - { - _spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL); - } + _spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL); } } diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index d7ba4ea470..5b9a449be1 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -449,7 +449,7 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content) std::string result = content["state"]; LLUUID new_id = content["new_asset"]; - llinfos << "result: " << result << "new_id:" << new_id << llendl; + llinfos << "result: " << result << " new_id: " << new_id << llendl; if (result == "complete" && mBakedUploadData != NULL) { // Invoke diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h index 70871b62e2..381b919c4a 100644 --- a/indra/newview/llassetuploadresponders.h +++ b/indra/newview/llassetuploadresponders.h @@ -112,6 +112,7 @@ private: struct LLBakedUploadData; class LLSendTexLayerResponder : public LLAssetUploadResponder { + LOG_CLASS(LLSendTexLayerResponder); public: LLSendTexLayerResponder(const LLSD& post_data, const LLUUID& vfile_id, diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 7848484ac6..5fd262a720 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -751,10 +751,7 @@ void LLFloaterPreference::onBtnOK() closeFloater(false); LLUIColorTable::instance().saveUserSettings(); - gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); - std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - // save all settings, even if equals defaults - gCrashSettings.saveToFile(crash_settings_filename, FALSE); + gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); } else { diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 07c7f35989..a4f6921f98 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -30,6 +30,7 @@ #include "llpanelface.h" // library includes +#include "llcalc.h" #include "llerror.h" #include "llfocusmgr.h" #include "llrect.h" @@ -926,6 +927,16 @@ void LLPanelFace::getState() getChildView("button apply")->setEnabled(enabled); } } + + // Set variable values for numeric expressions + LLCalc* calcp = LLCalc::getInstance(); + calcp->setVar(LLCalc::TEX_U_SCALE, childGetValue("TexScaleU").asReal()); + calcp->setVar(LLCalc::TEX_V_SCALE, childGetValue("TexScaleV").asReal()); + calcp->setVar(LLCalc::TEX_U_OFFSET, childGetValue("TexOffsetU").asReal()); + calcp->setVar(LLCalc::TEX_V_OFFSET, childGetValue("TexOffsetV").asReal()); + calcp->setVar(LLCalc::TEX_ROTATION, childGetValue("TexRot").asReal()); + calcp->setVar(LLCalc::TEX_TRANSPARENCY, childGetValue("ColorTrans").asReal()); + calcp->setVar(LLCalc::TEX_GLOW, childGetValue("glow").asReal()); } else { @@ -961,6 +972,16 @@ void LLPanelFace::getState() //getChildView("has media")->setEnabled(FALSE); //getChildView("media info set")->setEnabled(FALSE); + + // Set variable values for numeric expressions + LLCalc* calcp = LLCalc::getInstance(); + calcp->clearVar(LLCalc::TEX_U_SCALE); + calcp->clearVar(LLCalc::TEX_V_SCALE); + calcp->clearVar(LLCalc::TEX_U_OFFSET); + calcp->clearVar(LLCalc::TEX_V_OFFSET); + calcp->clearVar(LLCalc::TEX_ROTATION); + calcp->clearVar(LLCalc::TEX_TRANSPARENCY); + calcp->clearVar(LLCalc::TEX_GLOW); } } diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 52917ff20b..c222bbb191 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -41,6 +41,7 @@ // project includes #include "llagent.h" #include "llbutton.h" +#include "llcalc.h" #include "llcheckboxctrl.h" #include "llcolorswatch.h" #include "llcombobox.h" @@ -318,6 +319,8 @@ void LLPanelObject::getState( ) } } + LLCalc* calcp = LLCalc::getInstance(); + LLVOVolume *volobjp = NULL; if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) { @@ -334,6 +337,7 @@ void LLPanelObject::getState( ) // Disable all text input fields clearCtrls(); + calcp->clearAllVariables(); return; } @@ -360,12 +364,18 @@ void LLPanelObject::getState( ) mCtrlPosX->set( vec.mV[VX] ); mCtrlPosY->set( vec.mV[VY] ); mCtrlPosZ->set( vec.mV[VZ] ); + calcp->setVar(LLCalc::X_POS, vec.mV[VX]); + calcp->setVar(LLCalc::Y_POS, vec.mV[VY]); + calcp->setVar(LLCalc::Z_POS, vec.mV[VZ]); } else { mCtrlPosX->clear(); mCtrlPosY->clear(); mCtrlPosZ->clear(); + calcp->clearVar(LLCalc::X_POS); + calcp->clearVar(LLCalc::Y_POS); + calcp->clearVar(LLCalc::Z_POS); } @@ -380,12 +390,18 @@ void LLPanelObject::getState( ) mCtrlScaleX->set( vec.mV[VX] ); mCtrlScaleY->set( vec.mV[VY] ); mCtrlScaleZ->set( vec.mV[VZ] ); + calcp->setVar(LLCalc::X_SCALE, vec.mV[VX]); + calcp->setVar(LLCalc::Y_SCALE, vec.mV[VY]); + calcp->setVar(LLCalc::Z_SCALE, vec.mV[VZ]); } else { mCtrlScaleX->clear(); mCtrlScaleY->clear(); mCtrlScaleZ->clear(); + calcp->setVar(LLCalc::X_SCALE, 0.f); + calcp->setVar(LLCalc::Y_SCALE, 0.f); + calcp->setVar(LLCalc::Z_SCALE, 0.f); } mLabelSize->setEnabled( enable_scale ); @@ -405,12 +421,18 @@ void LLPanelObject::getState( ) mCtrlRotX->set( mCurEulerDegrees.mV[VX] ); mCtrlRotY->set( mCurEulerDegrees.mV[VY] ); mCtrlRotZ->set( mCurEulerDegrees.mV[VZ] ); + calcp->setVar(LLCalc::X_ROT, mCurEulerDegrees.mV[VX]); + calcp->setVar(LLCalc::Y_ROT, mCurEulerDegrees.mV[VY]); + calcp->setVar(LLCalc::Z_ROT, mCurEulerDegrees.mV[VZ]); } else { mCtrlRotX->clear(); mCtrlRotY->clear(); mCtrlRotZ->clear(); + calcp->clearVar(LLCalc::X_ROT); + calcp->clearVar(LLCalc::Y_ROT); + calcp->clearVar(LLCalc::Z_ROT); } mLabelRotation->setEnabled( enable_rotate ); @@ -625,9 +647,9 @@ void LLPanelObject::getState( ) F32 end_t = volume_params.getEndT(); // Hollowness - F32 hollow = volume_params.getHollow(); - mSpinHollow->set( 100.f * hollow ); - + F32 hollow = 100.f * volume_params.getHollow(); + mSpinHollow->set( hollow ); + calcp->setVar(LLCalc::HOLLOW, hollow); // All hollow objects allow a shape to be selected. if (hollow > 0.f) { @@ -679,6 +701,10 @@ void LLPanelObject::getState( ) mSpinCutEnd ->set( cut_end ); mCtrlPathBegin ->set( adv_cut_begin ); mCtrlPathEnd ->set( adv_cut_end ); + calcp->setVar(LLCalc::CUT_BEGIN, cut_begin); + calcp->setVar(LLCalc::CUT_END, cut_end); + calcp->setVar(LLCalc::PATH_BEGIN, adv_cut_begin); + calcp->setVar(LLCalc::PATH_END, adv_cut_end); // Twist F32 twist = volume_params.getTwist(); @@ -697,18 +723,24 @@ void LLPanelObject::getState( ) mSpinTwist ->set( twist ); mSpinTwistBegin ->set( twist_begin ); + calcp->setVar(LLCalc::TWIST_END, twist); + calcp->setVar(LLCalc::TWIST_BEGIN, twist_begin); // Shear F32 shear_x = volume_params.getShearX(); F32 shear_y = volume_params.getShearY(); mSpinShearX->set( shear_x ); mSpinShearY->set( shear_y ); + calcp->setVar(LLCalc::X_SHEAR, shear_x); + calcp->setVar(LLCalc::Y_SHEAR, shear_y); // Taper F32 taper_x = volume_params.getTaperX(); F32 taper_y = volume_params.getTaperY(); mSpinTaperX->set( taper_x ); mSpinTaperY->set( taper_y ); + calcp->setVar(LLCalc::X_TAPER, taper_x); + calcp->setVar(LLCalc::Y_TAPER, taper_y); // Radius offset. F32 radius_offset = volume_params.getRadiusOffset(); @@ -738,10 +770,12 @@ void LLPanelObject::getState( ) } } mSpinRadiusOffset->set( radius_offset); + calcp->setVar(LLCalc::RADIUS_OFFSET, radius_offset); // Revolutions F32 revolutions = volume_params.getRevolutions(); mSpinRevolutions->set( revolutions ); + calcp->setVar(LLCalc::REVOLUTIONS, revolutions); // Skew F32 skew = volume_params.getSkew(); @@ -766,6 +800,7 @@ void LLPanelObject::getState( ) } } mSpinSkew->set( skew ); + calcp->setVar(LLCalc::SKEW, skew); } // Compute control visibility, label names, and twist range. @@ -869,6 +904,8 @@ void LLPanelObject::getState( ) case MI_RING: mSpinScaleX->set( scale_x ); mSpinScaleY->set( scale_y ); + calcp->setVar(LLCalc::X_HOLE, scale_x); + calcp->setVar(LLCalc::Y_HOLE, scale_y); mSpinScaleX->setMinValue(OBJECT_MIN_HOLE_SIZE); mSpinScaleX->setMaxValue(OBJECT_MAX_HOLE_SIZE_X); mSpinScaleY->setMinValue(OBJECT_MIN_HOLE_SIZE); @@ -883,6 +920,14 @@ void LLPanelObject::getState( ) mSpinScaleX->setMaxValue(1.f); mSpinScaleY->setMinValue(-1.f); mSpinScaleY->setMaxValue(1.f); + + // Torus' Hole Size is Box/Cyl/Prism's Taper + calcp->setVar(LLCalc::X_TAPER, 1.f - scale_x); + calcp->setVar(LLCalc::Y_TAPER, 1.f - scale_y); + + // Box/Cyl/Prism have no hole size + calcp->setVar(LLCalc::X_HOLE, 0.f); + calcp->setVar(LLCalc::Y_HOLE, 0.f); } break; } diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index 0645fd8a54..f19b54c1d4 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -38,6 +38,8 @@ #include "llsidetray.h" #include "llviewermenu.h" #include "llwearableitemslist.h" +#include "llsdserialize.h" +#include "llclipboard.h" // Context menu and Gear menu helper. static void edit_outfit() @@ -58,6 +60,7 @@ public: registrar.add("Gear.Edit", boost::bind(&edit_outfit)); registrar.add("Gear.TakeOff", boost::bind(&LLWearingGearMenu::onTakeOff, this)); + registrar.add("Gear.Copy", boost::bind(&LLPanelWearing::copyToClipboard, mPanelWearing)); enable_registrar.add("Gear.OnEnable", boost::bind(&LLPanelWearing::isActionEnabled, mPanelWearing, _2)); @@ -280,4 +283,25 @@ void LLPanelWearing::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const mCOFItemsList->getSelectedUUIDs(selected_uuids); } +void LLPanelWearing::copyToClipboard() +{ + std::string text; + std::vector<LLSD> data; + mCOFItemsList->getValues(data); + + for(std::vector<LLSD>::const_iterator iter = data.begin(); iter != data.end();) + { + LLSD uuid = (*iter); + LLViewerInventoryItem* item = gInventory.getItem(uuid); + + iter++; + if (item != NULL) + { + // Append a newline to all but the last line + text += iter != data.end() ? item->getName() + "\n" : item->getName(); + } + } + + gClipboard.copyFromString(utf8str_to_wstring(text)); +} // EOF diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h index 157b2c4c5f..9a212b3cca 100644 --- a/indra/newview/llpanelwearing.h +++ b/indra/newview/llpanelwearing.h @@ -60,6 +60,8 @@ public: /*virtual*/ void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const; + /*virtual*/ void copyToClipboard(); + boost::signals2::connection setSelectionChangeCallback(commit_callback_t cb); bool hasItemSelected(); diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp index 500c2a7b86..bd41aa64f0 100644 --- a/indra/newview/lltexlayer.cpp +++ b/indra/newview/lltexlayer.cpp @@ -51,6 +51,9 @@ using namespace LLVOAvatarDefines; +static const S32 BAKE_UPLOAD_ATTEMPTS = 7; +static const F32 BAKE_UPLOAD_RETRY_DELAY = 2.f; // actual delay grows by power of 2 each attempt + class LLTexLayerInfo { friend class LLTexLayer; @@ -93,11 +96,13 @@ private: //----------------------------------------------------------------------------- LLBakedUploadData::LLBakedUploadData(const LLVOAvatarSelf* avatar, LLTexLayerSet* layerset, - const LLUUID& id) : + const LLUUID& id, + bool highest_res) : mAvatar(avatar), mTexLayerSet(layerset), mID(id), - mStartTime(LLFrameTimer::getTotalTime()) // Record starting time + mStartTime(LLFrameTimer::getTotalTime()), // Record starting time + mIsHighestRes(highest_res) { } @@ -116,6 +121,7 @@ LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner, mUploadPending(FALSE), // Not used for any logic here, just to sync sending of updates mNeedsUpload(FALSE), mNumLowresUploads(0), + mUploadFailCount(0), mNeedsUpdate(TRUE), mNumLowresUpdates(0), mTexLayerSet(owner) @@ -204,6 +210,7 @@ void LLTexLayerSetBuffer::cancelUpload() mNeedsUpload = FALSE; mUploadPending = FALSE; mNeedsUploadTimer.pause(); + mUploadRetryTimer.reset(); } void LLTexLayerSetBuffer::pushProjection() const @@ -356,25 +363,38 @@ BOOL LLTexLayerSetBuffer::isReadyToUpload() const if (!gAgentQueryManager.hasNoPendingQueries()) return FALSE; // Can't upload if there are pending queries. if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) return FALSE; // Don't upload if avatar is using composites. - // If we requested an upload and have the final LOD ready, then upload. - if (mTexLayerSet->isLocalTextureDataFinal()) return TRUE; - - // Upload if we've hit a timeout. Upload is a pretty expensive process so we need to make sure - // we aren't doing uploads too frequently. - const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout"); - if (texture_timeout != 0) + BOOL ready = FALSE; + if (mTexLayerSet->isLocalTextureDataFinal()) + { + // If we requested an upload and have the final LOD ready, upload (or wait a while if this is a retry) + if (mUploadFailCount == 0) + { + ready = TRUE; + } + else + { + ready = mUploadRetryTimer.getElapsedTimeF32() >= BAKE_UPLOAD_RETRY_DELAY * (1 << (mUploadFailCount - 1)); + } + } + else { - // The timeout period increases exponentially between every lowres upload in order to prevent - // spamming the server with frequent uploads. - const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads); + // Upload if we've hit a timeout. Upload is a pretty expensive process so we need to make sure + // we aren't doing uploads too frequently. + const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout"); + if (texture_timeout != 0) + { + // The timeout period increases exponentially between every lowres upload in order to prevent + // spamming the server with frequent uploads. + const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads); - // If we hit our timeout and have textures available at even lower resolution, then upload. - const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold; - const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable(); - if (has_lower_lod && is_upload_textures_timeout) return TRUE; + // If we hit our timeout and have textures available at even lower resolution, then upload. + const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold; + const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable(); + ready = has_lower_lod && is_upload_textures_timeout; + } } - return FALSE; + return ready; } BOOL LLTexLayerSetBuffer::isReadyToUpdate() const @@ -482,17 +502,20 @@ void LLTexLayerSetBuffer::doUpload() if (valid) { + const bool highest_lod = mTexLayerSet->isLocalTextureDataFinal(); // Baked_upload_data is owned by the responder and deleted after the request completes. LLBakedUploadData* baked_upload_data = new LLBakedUploadData(gAgentAvatarp, this->mTexLayerSet, - asset_id); + asset_id, + highest_lod); // upload ID is used to avoid overlaps, e.g. when the user rapidly makes two changes outside of Face Edit. mUploadID = asset_id; // Upload the image const std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture"); if(!url.empty() - && !LLPipeline::sForceOldBakedUpload) // toggle debug setting UploadBakedTexOld to change between the new caps method and old method + && !LLPipeline::sForceOldBakedUpload // toggle debug setting UploadBakedTexOld to change between the new caps method and old method + && (mUploadFailCount < (BAKE_UPLOAD_ATTEMPTS - 1))) // Try last ditch attempt via asset store if cap upload is failing. { LLSD body = LLSD::emptyMap(); // The responder will call LLTexLayerSetBuffer::onTextureUploadComplete() @@ -511,7 +534,6 @@ void LLTexLayerSetBuffer::doUpload() llinfos << "Baked texture upload via Asset Store." << llendl; } - const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal(); if (highest_lod) { // Sending the final LOD for the baked texture. All done, pause @@ -603,14 +625,15 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, { LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)userdata; - if ((result == 0) && - isAgentAvatarValid() && + if (isAgentAvatarValid() && !gAgentAvatarp->isDead() && (baked_upload_data->mAvatar == gAgentAvatarp) && // Sanity check: only the user's avatar should be uploading textures. (baked_upload_data->mTexLayerSet->hasComposite())) { LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mTexLayerSet->getComposite(); - + S32 failures = layerset_buffer->mUploadFailCount; + layerset_buffer->mUploadFailCount = 0; + if (layerset_buffer->mUploadID.isNull()) { // The upload got canceled, we should be in the @@ -626,20 +649,28 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, { // This is the upload we're currently waiting for. layerset_buffer->mUploadID.setNull(); + const std::string name(baked_upload_data->mTexLayerSet->getBodyRegionName()); + const std::string resolution = baked_upload_data->mIsHighestRes ? " full res " : " low res "; if (result >= 0) { - layerset_buffer->mUploadPending = FALSE; + layerset_buffer->mUploadPending = FALSE; // Allows sending of AgentSetAppearance later LLVOAvatarDefines::ETextureIndex baked_te = gAgentAvatarp->getBakedTE(layerset_buffer->mTexLayerSet); // Update baked texture info with the new UUID U64 now = LLFrameTimer::getTotalTime(); // Record starting time - llinfos << "Baked texture upload took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl; + llinfos << "Baked" << resolution << "texture upload for " << name << " took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl; gAgentAvatarp->setNewBakedTexture(baked_te, uuid); } else { - // Avatar appearance is changing, ignore the upload results - llinfos << "Baked upload failed. Reason: " << result << llendl; - // *FIX: retry upload after n seconds, asset server could be busy + ++failures; + S32 max_attempts = baked_upload_data->mIsHighestRes ? BAKE_UPLOAD_ATTEMPTS : 1; // only retry final bakes + llwarns << "Baked" << resolution << "texture upload for " << name << " failed (attempt " << failures << "/" << max_attempts << ")" << llendl; + if (failures < max_attempts) + { + layerset_buffer->mUploadFailCount = failures; + layerset_buffer->mUploadRetryTimer.start(); + layerset_buffer->requestUpload(); + } } } else diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h index 2d710d2dce..85dadb213c 100644 --- a/indra/newview/lltexlayer.h +++ b/indra/newview/lltexlayer.h @@ -312,6 +312,8 @@ private: BOOL mUploadPending; // Whether we have received back the new baked textures LLUUID mUploadID; // The current upload process (null if none). LLFrameTimer mNeedsUploadTimer; // Tracks time since upload was requested and performed. + S32 mUploadFailCount; // Number of consecutive upload failures + LLFrameTimer mUploadRetryTimer; // Tracks time since last upload failure. //-------------------------------------------------------------------- // Updates @@ -363,12 +365,14 @@ struct LLBakedUploadData { LLBakedUploadData(const LLVOAvatarSelf* avatar, LLTexLayerSet* layerset, - const LLUUID& id); + const LLUUID& id, + bool highest_res); ~LLBakedUploadData() {} const LLUUID mID; const LLVOAvatarSelf* mAvatar; // note: backlink only; don't LLPointer LLTexLayerSet* mTexLayerSet; const U64 mStartTime; // for measuring baked texture upload time + const bool mIsHighestRes; // whether this is a "final" bake, or intermediate low res }; #endif // LL_LLTEXLAYER_H diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 87ca80260f..b87ca1eaec 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -566,7 +566,7 @@ bool toggle_show_object_render_cost(const LLSD& newvalue) return true; } -void toggle_updater_service_active(LLControlVariable* control, const LLSD& new_value) +void toggle_updater_service_active(const LLSD& new_value) { if(new_value.asInteger()) { @@ -735,7 +735,7 @@ void settings_setup_listeners() gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2)); gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2)); gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2)); - gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(&toggle_updater_service_active); + gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(boost::bind(&toggle_updater_service_active, _2)); gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2)); gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2)); } diff --git a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml index 0ac2c14253..0e858ccf10 100644 --- a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml @@ -20,4 +20,11 @@ function="Gear.OnEnable" parameter="take_off" /> </menu_item_call> + <menu_item_call + label="Copy outfit list to clipboard" + layout="topleft" + name="copy"> + <on_click + function="Gear.Copy" /> + </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 1c22a5c02e..e639f0dc9d 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -268,7 +268,7 @@ height="23" layout="topleft" left_delta="50" - top_pad="5" + top_pad="5" name="updater_service_combobox" width="300"> <combo_box.item diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp index 51ff754c27..170babbb98 100644 --- a/indra/win_crash_logger/llcrashloggerwindows.cpp +++ b/indra/win_crash_logger/llcrashloggerwindows.cpp @@ -296,6 +296,7 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles() bool LLCrashLoggerWindows::mainLoop() { + llinfos << "CrashSubmitBehavior is " << mCrashBehavior << llendl; // Note: parent hwnd is 0 (the desktop). No dlg proc. See Petzold (5th ed) HexCalc example, Chapter 11, p529 // win_crash_logger.rc has been edited by hand. @@ -308,6 +309,7 @@ bool LLCrashLoggerWindows::mainLoop() if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND) { + llinfos << "Showing crash report submit progress window." << llendl; ShowWindow(gHwndProgress, SW_SHOW ); sendCrashLogs(); } @@ -354,7 +356,7 @@ bool LLCrashLoggerWindows::mainLoop() void LLCrashLoggerWindows::updateApplication(const std::string& message) { - LLCrashLogger::updateApplication(); + LLCrashLogger::updateApplication(message); if(!message.empty()) show_progress(message); update_messages(); } @@ -370,6 +372,3 @@ bool LLCrashLoggerWindows::cleanup() PostQuitMessage(0); return true; } - - - diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h index 24c564457c..5c45a998b3 100644 --- a/indra/win_crash_logger/llcrashloggerwindows.h +++ b/indra/win_crash_logger/llcrashloggerwindows.h @@ -41,7 +41,6 @@ public: virtual void updateApplication(const std::string& message = LLStringUtil::null); virtual bool cleanup(); virtual void gatherPlatformSpecificFiles(); - //void annotateCallStack(); void setHandle(HINSTANCE hInst) { mhInst = hInst; } private: void ProcessDlgItemText(HWND hWnd, int nIDDlgItem); diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp index 5c22053317..8e916ae437 100644 --- a/indra/win_crash_logger/win_crash_logger.cpp +++ b/indra/win_crash_logger/win_crash_logger.cpp @@ -24,51 +24,30 @@ * $/LicenseInfo$ */ -// win_crash_logger.cpp : Defines the entry point for the application. -// - -// Must be first include, precompiled headers. #include "linden_common.h" - #include "stdafx.h" - #include <stdlib.h> - #include "llcrashloggerwindows.h" - - -// -// Implementation -// - int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { - llinfos << "Starting crash reporter" << llendl; + llinfos << "Starting crash reporter." << llendl; LLCrashLoggerWindows app; app.setHandle(hInstance); - bool ok = app.init(); - if(!ok) + app.parseCommandOptions(__argc, __argv); + + if (! app.init()) { llwarns << "Unable to initialize application." << llendl; return -1; } - // Run the application main loop - if(!LLApp::isQuitting()) app.mainLoop(); - - if (!app.isError()) - { - // - // We don't want to do cleanup here if the error handler got called - - // the assumption is that the error handler is responsible for doing - // app cleanup if there was a problem. - // - app.cleanup(); - } + app.mainLoop(); + app.cleanup(); + llinfos << "Crash reporter finished normally." << llendl; return 0; } |