Add searchable/filterable attributes to BugSplat
New metadata file method. Like the previous method that only aplied to windows this is so far only available on PC and XBox BugSplat APIS. Unmlike the deprecated version, it is expected to be added to MacOS and crashpad "soon".master
parent
d75439985f
commit
2545ffd25a
|
|
@ -1691,6 +1691,15 @@ set(viewer_HEADER_FILES
|
|||
list(APPEND viewer_SOURCE_FILES llperfstats.cpp)
|
||||
list(APPEND viewer_HEADER_FILES llperfstats.h)
|
||||
|
||||
if (USE_BUGSPLAT)
|
||||
list(APPEND viewer_SOURCE_FILES
|
||||
bugsplatattributes.cpp
|
||||
)
|
||||
list(APPEND viewer_HEADER_FILES
|
||||
bugsplatattributes.h
|
||||
)
|
||||
endif (USE_BUGSPLAT)
|
||||
|
||||
# <FS:Ansariel> Flickr / Discord keys and fsversionvalues headers are generated in here
|
||||
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,132 @@
|
|||
#include "bugsplatattributes.h"
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
std::wstring BugSplatAttributes::mCrashContextFileName;
|
||||
|
||||
BugSplatAttributes& BugSplatAttributes::instance()
|
||||
{
|
||||
static BugSplatAttributes sInstance;
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
|
||||
std::wstring BugSplatAttributes::to_xml_token(const std::wstring& input)
|
||||
{
|
||||
// Example usage:
|
||||
// std::wstring token = to_xml_token(L"Bandwidth (kbit/s)");
|
||||
// The result should be: L"Bandwidth_kbit_per_s"
|
||||
std::wstring result;
|
||||
result.reserve(input.size() * 2); // Reserve some space, since we might insert more chars for "/"
|
||||
|
||||
for (wchar_t ch : input)
|
||||
{
|
||||
if (ch == L'/')
|
||||
{
|
||||
// Replace '/' with "_per_"
|
||||
result.append(L"_per_");
|
||||
}
|
||||
else if (std::iswalnum(ch) || ch == L'_' || ch == L'-')
|
||||
{
|
||||
// Letters, digits, underscore, and hyphen are okay
|
||||
result.push_back(ch);
|
||||
}
|
||||
else if (ch == L' ' || ch == L'(' || ch == L')')
|
||||
{
|
||||
// Replace spaces and parentheses with underscore
|
||||
result.push_back(L'_');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other characters map to underscore
|
||||
result.push_back(L'_');
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the first character is a letter or underscore:
|
||||
// If not, prepend underscore.
|
||||
if (result.empty() || !(std::iswalpha(result.front()) || result.front() == L'_'))
|
||||
{
|
||||
result.insert(result.begin(), L'_');
|
||||
}
|
||||
// trim trailing underscores
|
||||
while (!result.empty() && result.back() == L'_')
|
||||
{
|
||||
result.pop_back();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool BugSplatAttributes::writeToFile(const std::wstring& file_path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
// Write to a temporary file first
|
||||
std::wstring tmp_file = file_path + L".tmp";
|
||||
|
||||
{
|
||||
std::wofstream ofs(tmp_file, std::ios::out | std::ios::trunc);
|
||||
if (!ofs.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ofs << L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
ofs << L"<XmlCrashContext>\n";
|
||||
|
||||
// First, write out attributes that have an empty category (top-level)
|
||||
auto empty_category_it = mAttributes.find(L"");
|
||||
if (empty_category_it != mAttributes.end())
|
||||
{
|
||||
for (const auto& kv : empty_category_it->second)
|
||||
{
|
||||
const std::wstring& key = kv.first;
|
||||
const std::wstring& val = kv.second;
|
||||
ofs << L" <" << key << L">" << val << L"</" << key << L">\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Write out all other categories
|
||||
for (const auto& cat_pair : mAttributes)
|
||||
{
|
||||
const std::wstring& category = cat_pair.first;
|
||||
|
||||
// Skip empty category since we already handled it above
|
||||
if (category.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ofs << L" <" << category << L">\n";
|
||||
for (const auto& kv : cat_pair.second)
|
||||
{
|
||||
const std::wstring& key = kv.first;
|
||||
const std::wstring& val = kv.second;
|
||||
ofs << L" <" << key << L">" << val << L"</" << key << L">\n";
|
||||
}
|
||||
ofs << L" </" << category << L">\n";
|
||||
}
|
||||
|
||||
ofs << L"</XmlCrashContext>\n";
|
||||
|
||||
// Close the file before renaming
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
// Rename the temp file to the final file. If rename fails, leave the old file intact.
|
||||
std::error_code ec;
|
||||
std::filesystem::rename(tmp_file, file_path, ec);
|
||||
if (ec)
|
||||
{
|
||||
// Rename failed, remove the temp file and return false
|
||||
std::filesystem::remove(tmp_file, ec);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
#ifndef BUGSPLATATTRIBUTES_INCLUDED
|
||||
#define BUGSPLATATTRIBUTES_INCLUDED
|
||||
#ifdef LL_BUGSPLAT
|
||||
// Currently on Windows BugSplat supports the new attributes file, but it is expected to be added to Mac and Linux soon.
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
class BugSplatAttributes
|
||||
{
|
||||
public:
|
||||
// Obtain the singleton instance
|
||||
static BugSplatAttributes& instance();
|
||||
|
||||
template<typename T>
|
||||
void setAttribute(const std::wstring& key, const T& value, const std::wstring& category = L"FS")
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
const auto& xml_key = to_xml_token(key);
|
||||
if constexpr (std::is_same_v<T, std::wstring>)
|
||||
{
|
||||
// Already wide string
|
||||
mAttributes[category][xml_key] = value;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, const wchar_t*> || std::is_array_v<T>)
|
||||
{
|
||||
// Handle wide string literals and arrays (which includes string literals)
|
||||
mAttributes[category][xml_key] = std::wstring(value);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
// Convert narrow to wide
|
||||
std::wstring wide_value(value.begin(), value.end());
|
||||
mAttributes[category][xml_key] = wide_value;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, bool>)
|
||||
{
|
||||
// Convert boolean to "true"/"false"
|
||||
mAttributes[category][xml_key] = value ? L"true" : L"false";
|
||||
}
|
||||
else if constexpr (std::is_arithmetic_v<T>)
|
||||
{
|
||||
// Convert arithmetic types (int, float, double, etc.) to wstring
|
||||
mAttributes[category][xml_key] = std::to_wstring(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!sizeof(T*), "No known conversion for this type to std::wstring.");
|
||||
}
|
||||
}
|
||||
// Returns true on success, false on failure.
|
||||
bool writeToFile(const std::wstring& file_path);
|
||||
const static std::wstring& getCrashContextFileName() { return mCrashContextFileName; }
|
||||
static void setCrashContextFileName(const std::wstring& file_name) { mCrashContextFileName = file_name; }
|
||||
private:
|
||||
BugSplatAttributes() = default;
|
||||
~BugSplatAttributes() = default;
|
||||
BugSplatAttributes(const BugSplatAttributes&) = delete;
|
||||
BugSplatAttributes& operator=(const BugSplatAttributes&) = delete;
|
||||
std::wstring to_xml_token(const std::wstring& input);
|
||||
// Internal structure to hold attributes by category
|
||||
// For example:
|
||||
// attributes_["RuntimeProperties"]["CrashGUID"] = L"649F57B7EE6E92A0_0000"
|
||||
std::unordered_map<std::wstring, std::unordered_map<std::wstring, std::wstring>> mAttributes;
|
||||
std::mutex mMutex;
|
||||
static std::wstring mCrashContextFileName;
|
||||
};
|
||||
|
||||
#endif // LL_BUGSPLAT
|
||||
#endif // BUGSPLATATTRIBUTES_INCLUDED
|
||||
|
|
@ -289,7 +289,7 @@ using namespace LL;
|
|||
|
||||
#include "fsradar.h"
|
||||
#include "fsassetblacklist.h"
|
||||
|
||||
#include "bugsplatattributes.h"
|
||||
// #include "fstelemetry.h" // <FS:Beq> Tracy profiler support
|
||||
|
||||
#if LL_LINUX && LL_GTK
|
||||
|
|
@ -767,6 +767,10 @@ LLAppViewer::LLAppViewer()
|
|||
// MAINT-8917: don't create a dump directory just for the
|
||||
// static_debug_info.log file
|
||||
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
|
||||
// <FS:Beq> Improve Bugsplat tracking by using attribu
|
||||
std::wstring wlogdir(logdir.begin(), logdir.end());
|
||||
BugSplatAttributes::setCrashContextFileName(wlogdir + L"crash-context.xml");
|
||||
// </FS:Beq>
|
||||
# else // ! LL_BUGSPLAT
|
||||
// write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues.
|
||||
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
|
||||
|
|
@ -3762,6 +3766,10 @@ bool LLAppViewer::waitForUpdater()
|
|||
void LLAppViewer::writeDebugInfo(bool isStatic)
|
||||
{
|
||||
#if LL_WINDOWS && LL_BUGSPLAT
|
||||
// <FS:Beq> Improve Bugsplat tracking by using attributes for certain static data items.
|
||||
const LLSD& info = getViewerInfo();
|
||||
bugsplatAddStaticAttributes(info);
|
||||
// </FS:Beq>
|
||||
// bugsplat does not create dump folder and debug logs are written directly
|
||||
// to logs folder, so it conflicts with main instance
|
||||
if (mSecondInstance)
|
||||
|
|
@ -3970,10 +3978,17 @@ LLSD LLAppViewer::getViewerInfo() const
|
|||
info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION)));
|
||||
info["LIBCURL_VERSION"] = LLCore::LLHttp::getCURLVersion();
|
||||
// Settings
|
||||
|
||||
LLRect window_rect = gViewerWindow->getWindowRectRaw();
|
||||
info["WINDOW_WIDTH"] = window_rect.getWidth();
|
||||
info["WINDOW_HEIGHT"] = window_rect.getHeight();
|
||||
// <FS:Beq> gViewerWindow can be null on shutdown. Crashes if bugsplatt uses the info
|
||||
// LLRect window_rect = gViewerWindow->getWindowRectRaw();
|
||||
// info["WINDOW_WIDTH"] = window_rect.getWidth();
|
||||
// info["WINDOW_HEIGHT"] = window_rect.getHeight();
|
||||
if(gViewerWindow)
|
||||
{
|
||||
LLRect window_rect = gViewerWindow->getWindowRectRaw();
|
||||
info["WINDOW_WIDTH"] = window_rect.getWidth();
|
||||
info["WINDOW_HEIGHT"] = window_rect.getHeight();
|
||||
}
|
||||
// </FS:Beq>
|
||||
|
||||
// <FS> Custom sysinfo
|
||||
//info["FONT_SIZE_ADJUSTMENT"] = gSavedSettings.getF32("FontScreenDPI");
|
||||
|
|
@ -3994,7 +4009,7 @@ LLSD LLAppViewer::getViewerInfo() const
|
|||
info["J2C_VERSION"] = LLImageJ2C::getEngineInfo();
|
||||
bool want_fullname = true;
|
||||
info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : "Undefined";
|
||||
if(LLVoiceClient::getInstance()->voiceEnabled())
|
||||
if(LLStartUp::getStartupState() == STATE_STARTED && LLVoiceClient::getInstance()->voiceEnabled()) // <FS:Beq/> Calling this too earli leads to a nasty crash loop
|
||||
{
|
||||
LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion();
|
||||
const std::string build_version = version.mBuildVersion;
|
||||
|
|
@ -4086,7 +4101,13 @@ LLSD LLAppViewer::getViewerInfo() const
|
|||
}
|
||||
|
||||
// populate field for new local disk cache with some details
|
||||
info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo();
|
||||
// <FS:Beq> only populate if the cache is available
|
||||
// info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo();
|
||||
if (auto cache = LLDiskCache::getInstance(); cache)
|
||||
{
|
||||
info["DISK_CACHE_INFO"] = cache->getCacheInfo();
|
||||
}
|
||||
// </FS:Beq>
|
||||
|
||||
// <FS:PP> FIRE-4785: Current render quality setting in sysinfo / about floater
|
||||
switch (gSavedSettings.getU32("RenderQualityPerformance"))
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ protected:
|
|||
virtual void overrideDetectedHardware(); // <FS:Beq/> Override VRAM (and others in future?) consistently.
|
||||
virtual bool initSLURLHandler();
|
||||
virtual bool sendURLToOtherInstance(const std::string& url);
|
||||
virtual void bugsplatAddStaticAttributes(const LLSD& info) {/*empty*/}; // <FS:Beq/> create a NOOP base impl
|
||||
|
||||
virtual bool initParseCommandLine(LLCommandLineParser& clp)
|
||||
{ return true; } // Allow platforms to specify the command line args.
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
|
||||
// Bugsplat (http://bugsplat.com) crash reporting tool
|
||||
#ifdef LL_BUGSPLAT
|
||||
#include "bugsplatattributes.h"
|
||||
#include "BugSplat.h"
|
||||
#include "boost/json.hpp" // Boost.Json
|
||||
#include "llagent.h" // for agent location
|
||||
|
|
@ -164,6 +165,7 @@ namespace
|
|||
flavor = "oss";
|
||||
#endif
|
||||
sBugSplatSender->setDefaultUserEmail( WCSTR(STRINGIZE(LLOSInfo::instance().getOSStringSimple() << " (" << ADDRESS_SIZE << "-bit, flavor " << flavor <<")")));
|
||||
// BugSplatAttributes::instance().setAttribute(L"Flavor", flavor);
|
||||
// </FS:ND>
|
||||
|
||||
//<FS:ND/> Clear out username first, as we get some crashes that has the OS set as username, let's see if this fixes it. Use Crash.Linden as a usr can never have a "Linden"
|
||||
|
|
@ -195,7 +197,6 @@ namespace
|
|||
|
||||
// LL_ERRS message, when there is one
|
||||
sBugSplatSender->setDefaultUserDescription(WCSTR(LLError::getFatalMessage()));
|
||||
|
||||
sBugSplatSender->setAttribute(WCSTR(L"OS"), WCSTR(LLOSInfo::instance().getOSStringSimple())); // In case we ever stop using email for this
|
||||
sBugSplatSender->setAttribute(WCSTR(L"AppState"), WCSTR(LLStartUp::getStartupStateString()));
|
||||
sBugSplatSender->setAttribute(WCSTR(L"GL Vendor"), WCSTR(gGLManager.mGLVendor));
|
||||
|
|
@ -203,18 +204,41 @@ namespace
|
|||
sBugSplatSender->setAttribute(WCSTR(L"GPU Version"), WCSTR(gGLManager.mDriverVersionVendorString));
|
||||
sBugSplatSender->setAttribute(WCSTR(L"GL Renderer"), WCSTR(gGLManager.mGLRenderer));
|
||||
sBugSplatSender->setAttribute(WCSTR(L"VRAM"), WCSTR(STRINGIZE(gGLManager.mVRAM)));
|
||||
sBugSplatSender->setAttribute(WCSTR(L"RAM"), WCSTR(STRINGIZE(gSysMemory.getPhysicalMemoryKB().value())));
|
||||
// sBugSplatSender->setAttribute(WCSTR(L"RAM"), WCSTR(STRINGIZE(gSysMemory.getPhysicalMemoryKB().value())));
|
||||
// <FS:Beq> Improve bugsplpat reporting with attributes
|
||||
// sBugSplatSender->setDefaultUserDescription(WCSTR(LLError::getFatalMessage()));
|
||||
// sBugSplatSender->setAttribute(WCSTR(L"AppState"), WCSTR(LLStartUp::getStartupStateString()));
|
||||
|
||||
auto fatal_message = LLError::getFatalMessage();
|
||||
sBugSplatSender->setDefaultUserDescription(WCSTR(fatal_message));
|
||||
BugSplatAttributes::instance().setAttribute(L"FatalMessage", fatal_message); // <FS:Beq/> Store this additionally as an attribute in case user overwrites.
|
||||
// App state
|
||||
BugSplatAttributes::instance().setAttribute(L"AppState", LLStartUp::getStartupStateString());
|
||||
// Location
|
||||
// </FS:Beq>
|
||||
if (gAgent.getRegion())
|
||||
{
|
||||
// region location, when we have it
|
||||
LLVector3 loc = gAgent.getPositionAgent();
|
||||
sBugSplatSender->resetAppIdentifier(
|
||||
WCSTR(STRINGIZE(gAgent.getRegion()->getName()
|
||||
// <FS:Beq> Improve bugsplat reporting with attributes
|
||||
// LLVector3 loc = gAgent.getPositionAgent();
|
||||
// sBugSplatSender->resetAppIdentifier(
|
||||
// WCSTR(STRINGIZE(gAgent.getRegion()->getName()
|
||||
// << '/' << loc.mV[0]
|
||||
// << '/' << loc.mV[1]
|
||||
// << '/' << loc.mV[2])));
|
||||
const LLVector3 loc = gAgent.getPositionAgent();
|
||||
const auto & fullLocation = STRINGIZE(gAgent.getRegion()->getName()
|
||||
<< '/' << loc.mV[0]
|
||||
<< '/' << loc.mV[1]
|
||||
<< '/' << loc.mV[2])));
|
||||
<< '/' << loc.mV[2]);
|
||||
sBugSplatSender->resetAppIdentifier(WCSTR(fullLocation));
|
||||
BugSplatAttributes::instance().setAttribute(L"Location", std::string(fullLocation));
|
||||
// </FS:Beq>
|
||||
}
|
||||
// <FS:Beq> Improve bugsplat reporting with attributes
|
||||
LLAppViewer::instance()->writeDebugInfo();
|
||||
sBugSplatSender->sendAdditionalFile(WCSTR(BugSplatAttributes::getCrashContextFileName())); // <FS:Beq/> Add the new attributes file
|
||||
// </FS:Beq>
|
||||
} // MDSCB_EXCEPTIONCODE
|
||||
|
||||
return false;
|
||||
|
|
@ -619,6 +643,71 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
|
|||
}
|
||||
}
|
||||
#endif
|
||||
// <FS:Beq> Use the Attributes API on Windows to enhance crash metadata
|
||||
void LLAppViewerWin32::bugsplatAddStaticAttributes(const LLSD& info)
|
||||
{
|
||||
#ifdef LL_BUGSPLAT
|
||||
static bool write_statics = true;
|
||||
|
||||
auto& bugSplatMap = BugSplatAttributes::instance();
|
||||
|
||||
if (write_statics)
|
||||
{
|
||||
write_statics = false;
|
||||
auto multipleInstances = gDebugInfo["FoundOtherInstanceAtStartup"].asBoolean();
|
||||
bugSplatMap.setAttribute(L"MultipleInstance", multipleInstances);
|
||||
|
||||
bugSplatMap.setAttribute(L"GPU", info["GRAPHICS_CARD"].asString());
|
||||
bugSplatMap.setAttribute(L"GPU VRAM (MB)", info["GRAPHICS_CARD_MEMORY"].asInteger());
|
||||
bugSplatMap.setAttribute(L"GPU VRAM Detected (MB)", info["GRAPHICS_CARD_MEMORY_DETECTED"].asInteger());
|
||||
bugSplatMap.setAttribute(L"GPU VRAM (Budget)", info["VRAM_BUDGET_ENGLISH"].asInteger());
|
||||
|
||||
bugSplatMap.setAttribute(L"CPU", info["CPU"].asString());
|
||||
bugSplatMap.setAttribute(L"Graphics Driver", info["GRAPHICS_DRIVER_VERSION"].asString());
|
||||
bugSplatMap.setAttribute(L"CPU MHz", (S32)gSysCPU.getMHz()); //
|
||||
#ifdef USE_AVX2_OPTIMIZATION
|
||||
bugSplatMap.setAttribute(L"SIMD", L"AVX2");
|
||||
#elif USE_AVX_OPTIMIZATION
|
||||
bugSplatMap.setAttribute(L"SIMD", L"AVX");
|
||||
#else
|
||||
bugSplatMap.setAttribute(L"SIMD", L"SSE2");
|
||||
#endif
|
||||
// set physical ram integer as a string attribute
|
||||
bugSplatMap.setAttribute(L"Physical RAM (KB)", LLMemory::getMaxMemKB().value());
|
||||
bugSplatMap.setAttribute(L"OpenGL Version", info["OPENGL_VERSION"].asString());
|
||||
bugSplatMap.setAttribute(L"libcurl Version", info["LIBCURL_VERSION"].asString());
|
||||
bugSplatMap.setAttribute(L"J2C Decoder Version", info["J2C_VERSION"].asString());
|
||||
bugSplatMap.setAttribute(L"Audio Driver Version", info["AUDIO_DRIVER_VERSION"].asString());
|
||||
// bugSplatMap.setAttribute(L"CEF Info", info["LIBCEF_VERSION"].asString());
|
||||
bugSplatMap.setAttribute(L"LibVLC Version", info["LIBVLC_VERSION"].asString());
|
||||
bugSplatMap.setAttribute(L"Vivox Version", info["VOICE_VERSION"].asString());
|
||||
bugSplatMap.setAttribute(L"RLVa", info["RLV_VERSION"].asString());
|
||||
bugSplatMap.setAttribute(L"Mode", info["MODE"].asString());
|
||||
bugSplatMap.setAttribute(L"Skin", llformat("%s (%s)", info["SKIN"].asString().c_str(), info["THEME"].asString().c_str()));
|
||||
#if LL_DARWIN
|
||||
bugSplatMap.setAttribute(L"HiDPI", info["HIDPI"].asBoolean() ? L"Enabled" : L"Disabled");
|
||||
#endif
|
||||
}
|
||||
// These attributes are potentially dynamic
|
||||
bugSplatMap.setAttribute(L"Packets Lost", llformat("%.0f/%.0f (%.1f%%)", info["PACKETS_LOST"].asReal(), info["PACKETS_IN"].asReal(), info["PACKETS_PCT"].asReal()));
|
||||
bugSplatMap.setAttribute(L"Window Size", llformat("%sx%s px", info["WINDOW_WIDTH"].asString().c_str(), info["WINDOW_HEIGHT"].asString().c_str()));
|
||||
bugSplatMap.setAttribute(L"Draw Distance (m)", info["DRAW_DISTANCE"].asInteger());
|
||||
bugSplatMap.setAttribute(L"Bandwidth (kbit/s)", info["BANDWIDTH"].asInteger());
|
||||
bugSplatMap.setAttribute(L"LOD Factor", info["LOD"].asReal());
|
||||
bugSplatMap.setAttribute(L"Render quality", info["RENDERQUALITY_FSDATA_ENGLISH"].asString());
|
||||
bugSplatMap.setAttribute(L"Disk Cache", info["DISK_CACHE_INFO"].asString());
|
||||
|
||||
bugSplatMap.setAttribute(L"GridName", gDebugInfo["GridName"].asString());
|
||||
bugSplatMap.setAttribute(L"Available RAM (KB)", LLMemory::getAvailableMemKB().value());
|
||||
bugSplatMap.setAttribute(L"Allocated RAM (KB)", LLMemory::getAllocatedMemKB().value());
|
||||
|
||||
if (bugSplatMap.writeToFile(BugSplatAttributes::getCrashContextFileName()))
|
||||
{
|
||||
LL_INFOS() << "Crash context saved to " << WCSTR(BugSplatAttributes::getCrashContextFileName()) << LL_ENDL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// </FS:Beq>
|
||||
|
||||
void LLAppViewerWin32::disableWinErrorReporting()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ protected:
|
|||
|
||||
private:
|
||||
void disableWinErrorReporting();
|
||||
|
||||
void bugsplatAddStaticAttributes(const LLSD& info) override; // <FS:Beq> override for windows attributes
|
||||
std::string mCmdLine;
|
||||
bool mIsConsoleAllocated;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue