| File: | var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp |
| Warning: | line 189, column 5 Value stored to 'rv' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
| 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 6 | |
| 7 | #include "prio.h" |
| 8 | #include "PLDHashTable.h" |
| 9 | #include "mozilla/IOInterposer.h" |
| 10 | #include "mozilla/AutoMemMap.h" |
| 11 | #include "mozilla/IOBuffers.h" |
| 12 | #include "mozilla/MemoryReporting.h" |
| 13 | #include "mozilla/MemUtils.h" |
| 14 | #include "mozilla/MmapFaultHandler.h" |
| 15 | #include "mozilla/ResultExtensions.h" |
| 16 | #include "mozilla/scache/StartupCache.h" |
| 17 | #include "mozilla/ScopeExit.h" |
| 18 | #include "mozilla/Try.h" |
| 19 | |
| 20 | #include "nsClassHashtable.h" |
| 21 | #include "nsComponentManagerUtils.h" |
| 22 | #include "nsCRT.h" |
| 23 | #include "nsDirectoryServiceUtils.h" |
| 24 | #include "nsIClassInfo.h" |
| 25 | #include "nsIFile.h" |
| 26 | #include "nsIObserver.h" |
| 27 | #include "nsIOutputStream.h" |
| 28 | #include "nsISupports.h" |
| 29 | #include "nsITimer.h" |
| 30 | #include "mozilla/Omnijar.h" |
| 31 | #include "prenv.h" |
| 32 | #include "mozilla/Telemetry.h" |
| 33 | #include "nsThreadUtils.h" |
| 34 | #include "nsXULAppAPI.h" |
| 35 | #include "nsIProtocolHandler.h" |
| 36 | #include "GeckoProfiler.h" |
| 37 | #include "nsAppRunner.h" |
| 38 | #include "xpcpublic.h" |
| 39 | #ifdef MOZ_BACKGROUNDTASKS1 |
| 40 | # include "mozilla/BackgroundTasks.h" |
| 41 | #endif |
| 42 | |
| 43 | #if defined(XP_WIN) |
| 44 | # include <windows.h> |
| 45 | #endif |
| 46 | |
| 47 | #ifdef IS_BIG_ENDIAN |
| 48 | # define SC_ENDIAN"little" "big" |
| 49 | #else |
| 50 | # define SC_ENDIAN"little" "little" |
| 51 | #endif |
| 52 | |
| 53 | #if PR_BYTES_PER_WORD8 == 4 |
| 54 | # define SC_WORDSIZE"8" "4" |
| 55 | #else |
| 56 | # define SC_WORDSIZE"8" "8" |
| 57 | #endif |
| 58 | |
| 59 | using namespace mozilla::Compression; |
| 60 | |
| 61 | namespace mozilla { |
| 62 | namespace scache { |
| 63 | |
| 64 | MOZ_DEFINE_MALLOC_SIZE_OF(StartupCacheMallocSizeOf)static size_t StartupCacheMallocSizeOf(const void* aPtr) { mozilla ::dmd::Report(aPtr); return moz_malloc_size_of(aPtr); } |
| 65 | |
| 66 | NS_IMETHODIMPnsresult |
| 67 | StartupCache::CollectReports(nsIHandleReportCallback* aHandleReport, |
| 68 | nsISupports* aData, bool aAnonymize) { |
| 69 | MutexAutoLock lock(mTableLock); |
| 70 | MOZ_COLLECT_REPORT((void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/mapping" ), KIND_NONHEAP, UNITS_BYTES, mCacheData.nonHeapSizeOfExcludingThis (), nsLiteralCString("Memory used to hold the mapping of the startup cache from file. " "This memory is likely to be swapped out shortly after start-up." ), aData) |
| 71 | "explicit/startup-cache/mapping", KIND_NONHEAP, UNITS_BYTES,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/mapping" ), KIND_NONHEAP, UNITS_BYTES, mCacheData.nonHeapSizeOfExcludingThis (), nsLiteralCString("Memory used to hold the mapping of the startup cache from file. " "This memory is likely to be swapped out shortly after start-up." ), aData) |
| 72 | mCacheData.nonHeapSizeOfExcludingThis(),(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/mapping" ), KIND_NONHEAP, UNITS_BYTES, mCacheData.nonHeapSizeOfExcludingThis (), nsLiteralCString("Memory used to hold the mapping of the startup cache from file. " "This memory is likely to be swapped out shortly after start-up." ), aData) |
| 73 | "Memory used to hold the mapping of the startup cache from file. "(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/mapping" ), KIND_NONHEAP, UNITS_BYTES, mCacheData.nonHeapSizeOfExcludingThis (), nsLiteralCString("Memory used to hold the mapping of the startup cache from file. " "This memory is likely to be swapped out shortly after start-up." ), aData) |
| 74 | "This memory is likely to be swapped out shortly after start-up.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/mapping" ), KIND_NONHEAP, UNITS_BYTES, mCacheData.nonHeapSizeOfExcludingThis (), nsLiteralCString("Memory used to hold the mapping of the startup cache from file. " "This memory is likely to be swapped out shortly after start-up." ), aData); |
| 75 | |
| 76 | MOZ_COLLECT_REPORT("explicit/startup-cache/data", KIND_HEAP, UNITS_BYTES,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/data" ), KIND_HEAP, UNITS_BYTES, HeapSizeOfIncludingThis(StartupCacheMallocSizeOf ), nsLiteralCString("Memory used by the startup cache for things other than " "the file mapping."), aData) |
| 77 | HeapSizeOfIncludingThis(StartupCacheMallocSizeOf),(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/data" ), KIND_HEAP, UNITS_BYTES, HeapSizeOfIncludingThis(StartupCacheMallocSizeOf ), nsLiteralCString("Memory used by the startup cache for things other than " "the file mapping."), aData) |
| 78 | "Memory used by the startup cache for things other than "(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/data" ), KIND_HEAP, UNITS_BYTES, HeapSizeOfIncludingThis(StartupCacheMallocSizeOf ), nsLiteralCString("Memory used by the startup cache for things other than " "the file mapping."), aData) |
| 79 | "the file mapping.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/startup-cache/data" ), KIND_HEAP, UNITS_BYTES, HeapSizeOfIncludingThis(StartupCacheMallocSizeOf ), nsLiteralCString("Memory used by the startup cache for things other than " "the file mapping."), aData); |
| 80 | |
| 81 | return NS_OK; |
| 82 | } |
| 83 | |
| 84 | static const uint8_t MAGIC[] = "startupcache0002"; |
| 85 | // This is a heuristic value for how much to reserve for mTable to avoid |
| 86 | // rehashing. This is not a hard limit in release builds, but it is in |
| 87 | // debug builds as it should be stable. If we exceed this number we should |
| 88 | // just increase it. |
| 89 | static const size_t STARTUP_CACHE_RESERVE_CAPACITY = 450; |
| 90 | // This is a hard limit which we will assert on, to ensure that we don't |
| 91 | // have some bug causing runaway cache growth. |
| 92 | static const size_t STARTUP_CACHE_MAX_CAPACITY = 5000; |
| 93 | |
| 94 | // Not const because we change it for gtests. |
| 95 | static uint8_t STARTUP_CACHE_WRITE_TIMEOUT = 60; |
| 96 | |
| 97 | #define STARTUP_CACHE_NAME"startupCache." "8" "." "little" "startupCache." SC_WORDSIZE"8" "." SC_ENDIAN"little" |
| 98 | |
| 99 | static inline Result<Ok, nsresult> Write(PRFileDesc* fd, const void* data, |
| 100 | int32_t len) { |
| 101 | if (PR_Write(fd, data, len) != len) { |
| 102 | return Err(NS_ERROR_FAILURE); |
| 103 | } |
| 104 | return Ok(); |
| 105 | } |
| 106 | |
| 107 | static inline Result<Ok, nsresult> Seek(PRFileDesc* fd, int32_t offset) { |
| 108 | if (PR_Seek(fd, offset, PR_SEEK_SET) == -1) { |
| 109 | return Err(NS_ERROR_FAILURE); |
| 110 | } |
| 111 | return Ok(); |
| 112 | } |
| 113 | |
| 114 | static nsresult MapLZ4ErrorToNsresult(size_t aError) { |
| 115 | return NS_ERROR_FAILURE; |
| 116 | } |
| 117 | |
| 118 | StartupCache* StartupCache::GetSingletonNoInit() { |
| 119 | return StartupCache::gStartupCache; |
| 120 | } |
| 121 | |
| 122 | StartupCache* StartupCache::GetSingleton() { |
| 123 | #ifdef MOZ_BACKGROUNDTASKS1 |
| 124 | if (BackgroundTasks::IsBackgroundTaskMode()) { |
| 125 | return nullptr; |
| 126 | } |
| 127 | #endif |
| 128 | |
| 129 | if (!gStartupCache) { |
| 130 | if (!XRE_IsParentProcess()) { |
| 131 | return nullptr; |
| 132 | } |
| 133 | #ifdef MOZ_DISABLE_STARTUPCACHE |
| 134 | return nullptr; |
| 135 | #else |
| 136 | StartupCache::InitSingleton(); |
| 137 | #endif |
| 138 | } |
| 139 | |
| 140 | return StartupCache::gStartupCache; |
| 141 | } |
| 142 | |
| 143 | void StartupCache::DeleteSingleton() { StartupCache::gStartupCache = nullptr; } |
| 144 | |
| 145 | nsresult StartupCache::InitSingleton() { |
| 146 | nsresult rv; |
| 147 | StartupCache::gStartupCache = new StartupCache(); |
| 148 | |
| 149 | rv = StartupCache::gStartupCache->Init(); |
| 150 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 151 | StartupCache::gStartupCache = nullptr; |
| 152 | } |
| 153 | return rv; |
| 154 | } |
| 155 | |
| 156 | StaticRefPtr<StartupCache> StartupCache::gStartupCache; |
| 157 | bool StartupCache::gShutdownInitiated; |
| 158 | bool StartupCache::gIgnoreDiskCache; |
| 159 | bool StartupCache::gFoundDiskCacheOnInit; |
| 160 | |
| 161 | NS_IMPL_ISUPPORTS(StartupCache, nsIMemoryReporter)MozExternalRefCountType StartupCache::AddRef(void) { static_assert (!std::is_destructible_v<StartupCache>, "Reference-counted class " "StartupCache" " should not have a public destructor. " "Make this class's destructor non-public" ); do { static_assert( mozilla::detail::AssertionConditionType <decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0" " (" "illegal refcnt" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0" ") (" "illegal refcnt" ")"); do { *((volatile int*)__null) = 161; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("StartupCache" != nullptr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!("StartupCache" != nullptr))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"StartupCache\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"StartupCache\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 161; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread .AssertOwnership("StartupCache" " not thread-safe"); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this), (count), ("StartupCache" ), (uint32_t)(sizeof(*this))); return count; } MozExternalRefCountType StartupCache::Release(void) { do { static_assert( mozilla::detail ::AssertionConditionType<decltype(int32_t(mRefCnt) > 0) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(int32_t(mRefCnt) > 0))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0" " (" "dup release" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0" ") (" "dup release" ")"); do { *((volatile int*)__null) = 161 ; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("StartupCache" != nullptr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!("StartupCache" != nullptr))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"StartupCache\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"StartupCache\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 161; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread .AssertOwnership("StartupCache" " not thread-safe"); const char * const nametmp = "StartupCache"; nsrefcnt count = --mRefCnt; NS_LogRelease((this), (count), (nametmp)); if (count == 0) { mRefCnt = 1; delete (this); return 0; } return count; } nsresult StartupCache::QueryInterface(const nsIID& aIID, void** aInstancePtr ) { do { if (!(aInstancePtr)) { NS_DebugBreak(NS_DEBUG_ASSERTION , "QueryInterface requires a non-NULL destination!", "aInstancePtr" , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 161); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE ; static_assert(1 > 0, "Need more arguments to NS_INTERFACE_TABLE" ); static const QITableEntry table[] = { {&mozilla::detail ::kImplementedIID<StartupCache, nsIMemoryReporter>, int32_t ( reinterpret_cast<char*>(static_cast<nsIMemoryReporter *>((StartupCache*)0x1000)) - reinterpret_cast<char*> ((StartupCache*)0x1000))}, {&mozilla::detail::kImplementedIID <StartupCache, nsISupports>, int32_t(reinterpret_cast< char*>(static_cast<nsISupports*>( static_cast<nsIMemoryReporter *>((StartupCache*)0x1000))) - reinterpret_cast<char*> ((StartupCache*)0x1000))}, { nullptr, 0 } } ; static_assert(( sizeof(table) / sizeof(table[0])) > 1, "need at least 1 interface" ); rv = NS_TableDrivenQI(static_cast<void*>(this), aIID , aInstancePtr, table); return rv; } |
| 162 | |
| 163 | StartupCache::StartupCache() |
| 164 | : mTableLock("StartupCache::mTableLock"), |
| 165 | mDirty(false), |
| 166 | mWrittenOnce(false), |
| 167 | mCurTableReferenced(false), |
| 168 | mRequestedCount(0), |
| 169 | mCacheEntriesBaseOffset(0) {} |
| 170 | |
| 171 | StartupCache::~StartupCache() { UnregisterWeakMemoryReporter(this); } |
| 172 | |
| 173 | nsresult StartupCache::Init() { |
| 174 | // workaround for bug 653936 |
| 175 | nsCOMPtr<nsIProtocolHandler> jarInitializer( |
| 176 | do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"@mozilla.org/network/protocol;1?name=" "jar")); |
| 177 | |
| 178 | nsresult rv; |
| 179 | |
| 180 | if (mozilla::RunningGTest()) { |
| 181 | STARTUP_CACHE_WRITE_TIMEOUT = 3; |
| 182 | } |
| 183 | |
| 184 | // This allows to override the startup cache filename |
| 185 | // which is useful from xpcshell, when there is no ProfLDS directory to keep |
| 186 | // cache in. |
| 187 | char* env = PR_GetEnv("MOZ_STARTUP_CACHE"); |
| 188 | if (env && *env) { |
| 189 | rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, |
Value stored to 'rv' is never read | |
| 190 | getter_AddRefs(mFile)); |
| 191 | } else { |
| 192 | nsCOMPtr<nsIFile> file; |
| 193 | rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(file)); |
| 194 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 195 | // return silently, this will fail in mochitests's xpcshell process. |
| 196 | return rv; |
| 197 | } |
| 198 | |
| 199 | rv = file->AppendNative("startupCache"_ns); |
| 200 | NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl (__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName (__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with " "result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t >(__rv), name ? " (" : "", name ? name : "", name ? ")" : "" ); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 200); return rv; } } while (false); |
| 201 | |
| 202 | // Try to create the directory if it's not there yet |
| 203 | rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777); |
| 204 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) && rv != NS_ERROR_FILE_ALREADY_EXISTS) return rv; |
| 205 | |
| 206 | rv = file->AppendNative(nsLiteralCString(STARTUP_CACHE_NAME"startupCache." "8" "." "little")); |
| 207 | |
| 208 | NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl (__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName (__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with " "result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t >(__rv), name ? " (" : "", name ? name : "", name ? ")" : "" ); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 208); return rv; } } while (false); |
| 209 | |
| 210 | mFile = file; |
| 211 | } |
| 212 | |
| 213 | NS_ENSURE_TRUE(mFile, NS_ERROR_UNEXPECTED)do { if ((__builtin_expect(!!(!(mFile)), 0))) { NS_DebugBreak (NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mFile" ") failed", nullptr , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 213); return NS_ERROR_UNEXPECTED; } } while (false); |
| 214 | |
| 215 | mObserverService = do_GetService("@mozilla.org/observer-service;1"); |
| 216 | |
| 217 | if (!mObserverService) { |
| 218 | NS_WARNING("Could not get observerService.")NS_DebugBreak(NS_DEBUG_WARNING, "Could not get observerService." , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 218); |
| 219 | return NS_ERROR_UNEXPECTED; |
| 220 | } |
| 221 | |
| 222 | mListener = new StartupCacheListener(); |
| 223 | rv = mObserverService->AddObserver(mListener, NS_XPCOM_SHUTDOWN_OBSERVER_ID"xpcom-shutdown", |
| 224 | false); |
| 225 | NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl (__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName (__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with " "result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t >(__rv), name ? " (" : "", name ? name : "", name ? ")" : "" ); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 225); return rv; } } while (false); |
| 226 | rv = mObserverService->AddObserver(mListener, "startupcache-invalidate", |
| 227 | false); |
| 228 | NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl (__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName (__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with " "result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t >(__rv), name ? " (" : "", name ? name : "", name ? ")" : "" ); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 228); return rv; } } while (false); |
| 229 | rv = mObserverService->AddObserver(mListener, "intl:app-locales-changed", |
| 230 | false); |
| 231 | NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl (__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName (__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with " "result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t >(__rv), name ? " (" : "", name ? name : "", name ? ")" : "" ); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 231); return rv; } } while (false); |
| 232 | |
| 233 | { |
| 234 | MutexAutoLock lock(mTableLock); |
| 235 | auto result = LoadArchive(); |
| 236 | rv = result.isErr() ? result.unwrapErr() : NS_OK; |
| 237 | } |
| 238 | |
| 239 | gFoundDiskCacheOnInit = rv != NS_ERROR_FILE_NOT_FOUND; |
| 240 | |
| 241 | // Sometimes we don't have a cache yet, that's ok. |
| 242 | // If it's corrupted, just remove it and start over. |
| 243 | if (gIgnoreDiskCache || (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) && rv != NS_ERROR_FILE_NOT_FOUND)) { |
| 244 | NS_WARNING("Failed to load startupcache file correctly, removing!")NS_DebugBreak(NS_DEBUG_WARNING, "Failed to load startupcache file correctly, removing!" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 244); |
| 245 | InvalidateCache(); |
| 246 | } |
| 247 | |
| 248 | RegisterWeakMemoryReporter(this); |
| 249 | mDecompressionContext = MakeUnique<LZ4FrameDecompressionContext>(true); |
| 250 | |
| 251 | return NS_OK; |
| 252 | } |
| 253 | |
| 254 | void StartupCache::StartPrefetchMemory() { |
| 255 | { |
| 256 | MonitorAutoLock lock(mPrefetchComplete); |
| 257 | mPrefetchInProgress = true; |
| 258 | } |
| 259 | NS_DispatchBackgroundTask(NewRunnableMethod<uint8_t*, size_t>( |
| 260 | "StartupCache::ThreadedPrefetch", this, &StartupCache::ThreadedPrefetch, |
| 261 | mCacheData.get<uint8_t>().get(), mCacheData.size())); |
| 262 | } |
| 263 | |
| 264 | /** |
| 265 | * LoadArchive can only be called from the main thread. |
| 266 | */ |
| 267 | Result<Ok, nsresult> StartupCache::LoadArchive() { |
| 268 | MOZ_ASSERT(NS_IsMainThread(), "Can only load startup cache on main thread")do { static_assert( mozilla::detail::AssertionConditionType< decltype(NS_IsMainThread())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()" " (" "Can only load startup cache on main thread" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 268); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()" ") (" "Can only load startup cache on main thread" ")"); do { *((volatile int*)__null) = 268; __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
| 269 | if (gIgnoreDiskCache) return Err(NS_ERROR_FAILURE); |
| 270 | |
| 271 | MOZ_TRY(mCacheData.init(mFile))do { auto mozTryTempResult_ = ::mozilla::ToResult(mCacheData. init(mFile)); if ((__builtin_expect(!!(mozTryTempResult_.isErr ()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 272 | auto size = mCacheData.size(); |
| 273 | if (CanPrefetchMemory()) { |
| 274 | StartPrefetchMemory(); |
| 275 | } |
| 276 | |
| 277 | uint32_t headerSize; |
| 278 | if (size < sizeof(MAGIC) + sizeof(headerSize)) { |
| 279 | return Err(NS_ERROR_UNEXPECTED); |
| 280 | } |
| 281 | |
| 282 | auto data = mCacheData.get<uint8_t>(); |
| 283 | auto end = data + size; |
| 284 | |
| 285 | MMAP_FAULT_HANDLER_BEGIN_BUFFER(data.get(), size){ MmapAccessScope mmapScope((void*)(data.get()), (size)); if ( __sigsetjmp (mmapScope.mJmpBuf, 0) == 0) { |
| 286 | |
| 287 | if (memcmp(MAGIC, data.get(), sizeof(MAGIC))) { |
| 288 | return Err(NS_ERROR_UNEXPECTED); |
| 289 | } |
| 290 | data += sizeof(MAGIC); |
| 291 | |
| 292 | headerSize = LittleEndian::readUint32(data.get()); |
| 293 | data += sizeof(headerSize); |
| 294 | |
| 295 | if (headerSize > end - data) { |
| 296 | MOZ_ASSERT(false, "StartupCache file is corrupt.")do { static_assert( mozilla::detail::AssertionConditionType< decltype(false)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("false" " (" "StartupCache file is corrupt." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 296); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") (" "StartupCache file is corrupt." ")"); do { *((volatile int*) __null) = 296; __attribute__((nomerge)) ::abort(); } while (false ); } } while (false); |
| 297 | return Err(NS_ERROR_UNEXPECTED); |
| 298 | } |
| 299 | |
| 300 | Range<uint8_t> header(data, data + headerSize); |
| 301 | data += headerSize; |
| 302 | |
| 303 | mCacheEntriesBaseOffset = sizeof(MAGIC) + sizeof(headerSize) + headerSize; |
| 304 | { |
| 305 | if (!mTable.reserve(STARTUP_CACHE_RESERVE_CAPACITY)) { |
| 306 | return Err(NS_ERROR_UNEXPECTED); |
| 307 | } |
| 308 | auto cleanup = MakeScopeExit([&]() { |
| 309 | mTableLock.AssertCurrentThreadOwns(); |
| 310 | WaitOnPrefetch(); |
| 311 | mTable.clear(); |
| 312 | mCacheData.reset(); |
| 313 | }); |
| 314 | loader::InputBuffer buf(header); |
| 315 | |
| 316 | uint32_t currentOffset = 0; |
| 317 | while (!buf.finished()) { |
| 318 | uint32_t offset = 0; |
| 319 | uint32_t compressedSize = 0; |
| 320 | uint32_t uncompressedSize = 0; |
| 321 | nsCString key; |
| 322 | buf.codeUint32(offset); |
| 323 | buf.codeUint32(compressedSize); |
| 324 | buf.codeUint32(uncompressedSize); |
| 325 | buf.codeString(key); |
| 326 | |
| 327 | if (offset + compressedSize > end - data) { |
| 328 | MOZ_ASSERT(false, "StartupCache file is corrupt.")do { static_assert( mozilla::detail::AssertionConditionType< decltype(false)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("false" " (" "StartupCache file is corrupt." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") (" "StartupCache file is corrupt." ")"); do { *((volatile int*) __null) = 328; __attribute__((nomerge)) ::abort(); } while (false ); } } while (false); |
| 329 | return Err(NS_ERROR_UNEXPECTED); |
| 330 | } |
| 331 | |
| 332 | // Make sure offsets match what we'd expect based on script ordering and |
| 333 | // size, as a basic sanity check. |
| 334 | if (offset != currentOffset) { |
| 335 | return Err(NS_ERROR_UNEXPECTED); |
| 336 | } |
| 337 | currentOffset += compressedSize; |
| 338 | |
| 339 | // We could use mTable.putNew if we knew the file we're loading weren't |
| 340 | // corrupt. However, we don't know that, so check if the key already |
| 341 | // exists. If it does, we know the file must be corrupt. |
| 342 | decltype(mTable)::AddPtr p = mTable.lookupForAdd(key); |
| 343 | if (p) { |
| 344 | return Err(NS_ERROR_UNEXPECTED); |
| 345 | } |
| 346 | |
| 347 | if (!mTable.add( |
| 348 | p, key, |
| 349 | StartupCacheEntry(offset, compressedSize, uncompressedSize))) { |
| 350 | return Err(NS_ERROR_UNEXPECTED); |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | if (buf.error()) { |
| 355 | return Err(NS_ERROR_UNEXPECTED); |
| 356 | } |
| 357 | |
| 358 | cleanup.release(); |
| 359 | } |
| 360 | |
| 361 | MMAP_FAULT_HANDLER_CATCH(Err(NS_ERROR_UNEXPECTED))} else { NS_DebugBreak(NS_DEBUG_WARNING, "SIGBUS received when accessing mmapped file" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 361); return Err(NS_ERROR_UNEXPECTED); } } |
| 362 | |
| 363 | return Ok(); |
| 364 | } |
| 365 | |
| 366 | bool StartupCache::HasEntry(const char* id) { |
| 367 | AUTO_PROFILER_LABEL("StartupCache::HasEntry", OTHER)mozilla::AutoProfilerLabel raiiObject367( "StartupCache::HasEntry" , nullptr, JS::ProfilingCategoryPair::OTHER); |
| 368 | |
| 369 | MOZ_ASSERT(NS_IsMainThread(), "Startup cache only available on main thread")do { static_assert( mozilla::detail::AssertionConditionType< decltype(NS_IsMainThread())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()" " (" "Startup cache only available on main thread" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 369); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()" ") (" "Startup cache only available on main thread" ")"); do { *((volatile int*)__null) = 369; __attribute__((nomerge)) :: abort(); } while (false); } } while (false); |
| 370 | |
| 371 | MutexAutoLock lock(mTableLock); |
| 372 | return mTable.has(nsDependentCString(id)); |
| 373 | } |
| 374 | |
| 375 | nsresult StartupCache::GetBuffer(const char* id, const char** outbuf, |
| 376 | uint32_t* length) |
| 377 | MOZ_NO_THREAD_SAFETY_ANALYSIS__attribute__((no_thread_safety_analysis)) { |
| 378 | AUTO_PROFILER_LABEL("StartupCache::GetBuffer", OTHER)mozilla::AutoProfilerLabel raiiObject378( "StartupCache::GetBuffer" , nullptr, JS::ProfilingCategoryPair::OTHER); |
| 379 | |
| 380 | NS_ASSERTION(NS_IsMainThread(),do { if (!(NS_IsMainThread())) { NS_DebugBreak(NS_DEBUG_ASSERTION , "Startup cache only available on main thread", "NS_IsMainThread()" , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 381); MOZ_PretendNoReturn(); } } while (0) |
| 381 | "Startup cache only available on main thread")do { if (!(NS_IsMainThread())) { NS_DebugBreak(NS_DEBUG_ASSERTION , "Startup cache only available on main thread", "NS_IsMainThread()" , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 381); MOZ_PretendNoReturn(); } } while (0); |
| 382 | |
| 383 | Telemetry::LABELS_STARTUP_CACHE_REQUESTS label = |
| 384 | Telemetry::LABELS_STARTUP_CACHE_REQUESTS::Miss; |
| 385 | auto telemetry = |
| 386 | MakeScopeExit([&label] { Telemetry::AccumulateCategorical(label); }); |
| 387 | |
| 388 | MutexAutoLock lock(mTableLock); |
| 389 | decltype(mTable)::Ptr p = mTable.lookup(nsDependentCString(id)); |
| 390 | if (!p) { |
| 391 | return NS_ERROR_NOT_AVAILABLE; |
| 392 | } |
| 393 | |
| 394 | auto& value = p->value(); |
| 395 | if (value.mData) { |
| 396 | label = Telemetry::LABELS_STARTUP_CACHE_REQUESTS::HitMemory; |
| 397 | } else { |
| 398 | if (!mCacheData.initialized()) { |
| 399 | return NS_ERROR_NOT_AVAILABLE; |
| 400 | } |
| 401 | // It is impossible for a write to be pending here. This is because |
| 402 | // we just checked mCacheData.initialized(), and this is reset before |
| 403 | // writing to the cache. It's not re-initialized unless we call |
| 404 | // LoadArchive(), either from Init() (which must have already happened) or |
| 405 | // InvalidateCache(). InvalidateCache() locks the mutex, so a write can't be |
| 406 | // happening. |
| 407 | // Also, WriteToDisk() requires mTableLock, so while it's writing we can't |
| 408 | // be here. |
| 409 | |
| 410 | size_t totalRead = 0; |
| 411 | size_t totalWritten = 0; |
| 412 | Span<const char> compressed = Span( |
| 413 | mCacheData.get<char>().get() + mCacheEntriesBaseOffset + value.mOffset, |
| 414 | value.mCompressedSize); |
| 415 | value.mData = UniqueFreePtr<char[]>(reinterpret_cast<char*>( |
| 416 | malloc(sizeof(char) * value.mUncompressedSize))); |
| 417 | Span<char> uncompressed = Span(value.mData.get(), value.mUncompressedSize); |
| 418 | MMAP_FAULT_HANDLER_BEGIN_BUFFER(uncompressed.Elements(),{ MmapAccessScope mmapScope((void*)(uncompressed.Elements()), (uncompressed.Length())); if (__sigsetjmp (mmapScope.mJmpBuf , 0) == 0) { |
| 419 | uncompressed.Length()){ MmapAccessScope mmapScope((void*)(uncompressed.Elements()), (uncompressed.Length())); if (__sigsetjmp (mmapScope.mJmpBuf , 0) == 0) { |
| 420 | bool finished = false; |
| 421 | while (!finished) { |
| 422 | auto result = mDecompressionContext->Decompress( |
| 423 | uncompressed.From(totalWritten), compressed.From(totalRead)); |
| 424 | if (NS_WARN_IF(result.isErr())NS_warn_if_impl(result.isErr(), "result.isErr()", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 424)) { |
| 425 | value.mData = nullptr; |
| 426 | MutexAutoUnlock unlock(mTableLock); |
| 427 | InvalidateCache(); |
| 428 | return NS_ERROR_FAILURE; |
| 429 | } |
| 430 | auto decompressionResult = result.unwrap(); |
| 431 | totalRead += decompressionResult.mSizeRead; |
| 432 | totalWritten += decompressionResult.mSizeWritten; |
| 433 | finished = decompressionResult.mFinished; |
| 434 | } |
| 435 | |
| 436 | MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE)} else { NS_DebugBreak(NS_DEBUG_WARNING, "SIGBUS received when accessing mmapped file" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 436); return NS_ERROR_FAILURE; } } |
| 437 | |
| 438 | label = Telemetry::LABELS_STARTUP_CACHE_REQUESTS::HitDisk; |
| 439 | } |
| 440 | |
| 441 | if (!value.mRequested) { |
| 442 | value.mRequested = true; |
| 443 | value.mRequestedOrder = ++mRequestedCount; |
| 444 | MOZ_ASSERT(mRequestedCount <= mTable.count(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(mRequestedCount <= mTable.count())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mRequestedCount <= mTable .count()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mRequestedCount <= mTable.count()" " (" "Somehow we requested more StartupCache items than exist." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 445); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRequestedCount <= mTable.count()" ") (" "Somehow we requested more StartupCache items than exist." ")"); do { *((volatile int*)__null) = 445; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 445 | "Somehow we requested more StartupCache items than exist.")do { static_assert( mozilla::detail::AssertionConditionType< decltype(mRequestedCount <= mTable.count())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mRequestedCount <= mTable .count()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mRequestedCount <= mTable.count()" " (" "Somehow we requested more StartupCache items than exist." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 445); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRequestedCount <= mTable.count()" ") (" "Somehow we requested more StartupCache items than exist." ")"); do { *((volatile int*)__null) = 445; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 446 | ResetStartupWriteTimerCheckingReadCount(); |
| 447 | } |
| 448 | |
| 449 | // Track that something holds a reference into mTable, so we know to hold |
| 450 | // onto it in case the cache is invalidated. |
| 451 | mCurTableReferenced = true; |
| 452 | *outbuf = value.mData.get(); |
| 453 | *length = value.mUncompressedSize; |
| 454 | return NS_OK; |
| 455 | } |
| 456 | |
| 457 | // Makes a copy of the buffer, client retains ownership of inbuf. |
| 458 | nsresult StartupCache::PutBuffer(const char* id, UniqueFreePtr<char[]>&& inbuf, |
| 459 | uint32_t len) MOZ_NO_THREAD_SAFETY_ANALYSIS__attribute__((no_thread_safety_analysis)) { |
| 460 | NS_ASSERTION(NS_IsMainThread(),do { if (!(NS_IsMainThread())) { NS_DebugBreak(NS_DEBUG_ASSERTION , "Startup cache only available on main thread", "NS_IsMainThread()" , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 461); MOZ_PretendNoReturn(); } } while (0) |
| 461 | "Startup cache only available on main thread")do { if (!(NS_IsMainThread())) { NS_DebugBreak(NS_DEBUG_ASSERTION , "Startup cache only available on main thread", "NS_IsMainThread()" , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 461); MOZ_PretendNoReturn(); } } while (0); |
| 462 | if (StartupCache::gShutdownInitiated) { |
| 463 | return NS_ERROR_NOT_AVAILABLE; |
| 464 | } |
| 465 | |
| 466 | // Try to gain the table write lock. If the background task to write the |
| 467 | // cache is running, this will fail. |
| 468 | MutexAutoTryLock lock(mTableLock); |
| 469 | if (!lock) { |
| 470 | return NS_ERROR_NOT_AVAILABLE; |
| 471 | } |
| 472 | mTableLock.AssertCurrentThreadOwns(); |
| 473 | bool exists = mTable.has(nsDependentCString(id)); |
| 474 | if (exists) { |
| 475 | NS_WARNING("Existing entry in StartupCache.")NS_DebugBreak(NS_DEBUG_WARNING, "Existing entry in StartupCache." , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 475); |
| 476 | // Double-caching is undesirable but not an error. |
| 477 | return NS_OK; |
| 478 | } |
| 479 | |
| 480 | // putNew returns false on alloc failure - in the very unlikely event we hit |
| 481 | // that and aren't going to crash elsewhere, there's no reason we need to |
| 482 | // crash here. |
| 483 | if (mTable.putNew(nsCString(id), StartupCacheEntry(std::move(inbuf), len, |
| 484 | ++mRequestedCount))) { |
| 485 | return ResetStartupWriteTimer(); |
| 486 | } |
| 487 | MOZ_DIAGNOSTIC_ASSERT(mTable.count() < STARTUP_CACHE_MAX_CAPACITY,do { static_assert( mozilla::detail::AssertionConditionType< decltype(mTable.count() < STARTUP_CACHE_MAX_CAPACITY)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mTable.count() < STARTUP_CACHE_MAX_CAPACITY))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("mTable.count() < STARTUP_CACHE_MAX_CAPACITY" " (" "Too many StartupCache entries." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 488); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "mTable.count() < STARTUP_CACHE_MAX_CAPACITY" ") (" "Too many StartupCache entries." ")"); do { *((volatile int*)__null) = 488; __attribute__((nomerge)) ::abort(); } while (false); } } while (false) |
| 488 | "Too many StartupCache entries.")do { static_assert( mozilla::detail::AssertionConditionType< decltype(mTable.count() < STARTUP_CACHE_MAX_CAPACITY)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mTable.count() < STARTUP_CACHE_MAX_CAPACITY))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("mTable.count() < STARTUP_CACHE_MAX_CAPACITY" " (" "Too many StartupCache entries." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 488); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "mTable.count() < STARTUP_CACHE_MAX_CAPACITY" ") (" "Too many StartupCache entries." ")"); do { *((volatile int*)__null) = 488; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
| 489 | return NS_OK; |
| 490 | } |
| 491 | |
| 492 | size_t StartupCache::HeapSizeOfIncludingThis( |
| 493 | mozilla::MallocSizeOf aMallocSizeOf) const { |
| 494 | // This function could measure more members, but they haven't been found by |
| 495 | // DMD to be significant. They can be added later if necessary. |
| 496 | |
| 497 | size_t n = aMallocSizeOf(this); |
| 498 | |
| 499 | n += mTable.shallowSizeOfExcludingThis(aMallocSizeOf); |
| 500 | for (auto iter = mTable.iter(); !iter.done(); iter.next()) { |
| 501 | if (iter.get().value().mData) { |
| 502 | n += aMallocSizeOf(iter.get().value().mData.get()); |
| 503 | } |
| 504 | n += iter.get().key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
| 505 | } |
| 506 | |
| 507 | return n; |
| 508 | } |
| 509 | |
| 510 | /** |
| 511 | * WriteToDisk writes the cache out to disk. Callers of WriteToDisk need to call |
| 512 | * WaitOnWriteComplete to make sure there isn't a write |
| 513 | * happening on another thread. |
| 514 | * We own the mTableLock here. |
| 515 | */ |
| 516 | Result<Ok, nsresult> StartupCache::WriteToDisk() { |
| 517 | if (!mDirty || mWrittenOnce) { |
| 518 | return Ok(); |
| 519 | } |
| 520 | |
| 521 | if (!mFile) { |
| 522 | return Err(NS_ERROR_UNEXPECTED); |
| 523 | } |
| 524 | |
| 525 | AutoFDClose raiiFd; |
| 526 | MOZ_TRY(mFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,do { auto mozTryTempResult_ = ::mozilla::ToResult(mFile->OpenNSPRFileDesc (0x02 | 0x08 | 0x20, 0644, getter_Transfers(raiiFd))); if ((__builtin_expect (!!(mozTryTempResult_.isErr()), 0))) { return mozTryTempResult_ .propagateErr(); } } while (0) |
| 527 | 0644, getter_Transfers(raiiFd)))do { auto mozTryTempResult_ = ::mozilla::ToResult(mFile->OpenNSPRFileDesc (0x02 | 0x08 | 0x20, 0644, getter_Transfers(raiiFd))); if ((__builtin_expect (!!(mozTryTempResult_.isErr()), 0))) { return mozTryTempResult_ .propagateErr(); } } while (0); |
| 528 | const auto fd = raiiFd.get(); |
| 529 | |
| 530 | nsTArray<StartupCacheEntry::KeyValuePair> entries(mTable.count()); |
| 531 | for (auto iter = mTable.iter(); !iter.done(); iter.next()) { |
| 532 | if (iter.get().value().mRequested) { |
| 533 | StartupCacheEntry::KeyValuePair kv(&iter.get().key(), |
| 534 | &iter.get().value()); |
| 535 | entries.AppendElement(kv); |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | if (entries.IsEmpty()) { |
| 540 | return Ok(); |
| 541 | } |
| 542 | |
| 543 | entries.Sort(StartupCacheEntry::Comparator()); |
| 544 | loader::OutputBuffer buf; |
| 545 | for (auto& e : entries) { |
| 546 | auto* key = e.first; |
| 547 | auto* value = e.second; |
| 548 | auto uncompressedSize = value->mUncompressedSize; |
| 549 | // Set the mHeaderOffsetInFile so we can go back and edit the offset. |
| 550 | value->mHeaderOffsetInFile = buf.cursor(); |
| 551 | // Write a 0 offset/compressed size as a placeholder until we get the real |
| 552 | // offset after compressing. |
| 553 | buf.codeUint32(0); |
| 554 | buf.codeUint32(0); |
| 555 | buf.codeUint32(uncompressedSize); |
| 556 | buf.codeString(*key); |
| 557 | } |
| 558 | |
| 559 | uint8_t headerSize[4]; |
| 560 | LittleEndian::writeUint32(headerSize, buf.cursor()); |
| 561 | |
| 562 | MOZ_TRY(Write(fd, MAGIC, sizeof(MAGIC)))do { auto mozTryTempResult_ = ::mozilla::ToResult(Write(fd, MAGIC , sizeof(MAGIC))); if ((__builtin_expect(!!(mozTryTempResult_ .isErr()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 563 | MOZ_TRY(Write(fd, headerSize, sizeof(headerSize)))do { auto mozTryTempResult_ = ::mozilla::ToResult(Write(fd, headerSize , sizeof(headerSize))); if ((__builtin_expect(!!(mozTryTempResult_ .isErr()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 564 | size_t headerStart = sizeof(MAGIC) + sizeof(headerSize); |
| 565 | size_t dataStart = headerStart + buf.cursor(); |
| 566 | MOZ_TRY(Seek(fd, dataStart))do { auto mozTryTempResult_ = ::mozilla::ToResult(Seek(fd, dataStart )); if ((__builtin_expect(!!(mozTryTempResult_.isErr()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 567 | |
| 568 | size_t offset = 0; |
| 569 | |
| 570 | const size_t chunkSize = 1024 * 16; |
| 571 | LZ4FrameCompressionContext ctx(6, /* aCompressionLevel */ |
| 572 | chunkSize, /* aReadBufLen */ |
| 573 | true, /* aChecksum */ |
| 574 | true); /* aStableSrc */ |
| 575 | size_t writeBufLen = ctx.GetRequiredWriteBufferLength(); |
| 576 | auto writeBuffer = MakeUnique<char[]>(writeBufLen); |
| 577 | auto writeSpan = Span(writeBuffer.get(), writeBufLen); |
| 578 | |
| 579 | for (auto& e : entries) { |
| 580 | auto value = e.second; |
| 581 | value->mOffset = offset; |
| 582 | Span<const char> result; |
| 583 | MOZ_TRY_VAR(result,do { auto mozTryVarTempResult_ = (ctx.BeginCompressing(writeSpan ).mapErr(MapLZ4ErrorToNsresult)); if ((__builtin_expect(!!(mozTryVarTempResult_ .isErr()), 0))) { return mozTryVarTempResult_.propagateErr(); } (result) = mozTryVarTempResult_.unwrap(); } while (0) |
| 584 | ctx.BeginCompressing(writeSpan).mapErr(MapLZ4ErrorToNsresult))do { auto mozTryVarTempResult_ = (ctx.BeginCompressing(writeSpan ).mapErr(MapLZ4ErrorToNsresult)); if ((__builtin_expect(!!(mozTryVarTempResult_ .isErr()), 0))) { return mozTryVarTempResult_.propagateErr(); } (result) = mozTryVarTempResult_.unwrap(); } while (0); |
| 585 | MOZ_TRY(Write(fd, result.Elements(), result.Length()))do { auto mozTryTempResult_ = ::mozilla::ToResult(Write(fd, result .Elements(), result.Length())); if ((__builtin_expect(!!(mozTryTempResult_ .isErr()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 586 | offset += result.Length(); |
| 587 | |
| 588 | for (size_t i = 0; i < value->mUncompressedSize; i += chunkSize) { |
| 589 | size_t size = std::min(chunkSize, value->mUncompressedSize - i); |
| 590 | char* uncompressed = value->mData.get() + i; |
| 591 | MOZ_TRY_VAR(result, ctx.ContinueCompressing(Span(uncompressed, size))do { auto mozTryVarTempResult_ = (ctx.ContinueCompressing(Span (uncompressed, size)) .mapErr(MapLZ4ErrorToNsresult)); if ((__builtin_expect (!!(mozTryVarTempResult_.isErr()), 0))) { return mozTryVarTempResult_ .propagateErr(); } (result) = mozTryVarTempResult_.unwrap(); } while (0) |
| 592 | .mapErr(MapLZ4ErrorToNsresult))do { auto mozTryVarTempResult_ = (ctx.ContinueCompressing(Span (uncompressed, size)) .mapErr(MapLZ4ErrorToNsresult)); if ((__builtin_expect (!!(mozTryVarTempResult_.isErr()), 0))) { return mozTryVarTempResult_ .propagateErr(); } (result) = mozTryVarTempResult_.unwrap(); } while (0); |
| 593 | MOZ_TRY(Write(fd, result.Elements(), result.Length()))do { auto mozTryTempResult_ = ::mozilla::ToResult(Write(fd, result .Elements(), result.Length())); if ((__builtin_expect(!!(mozTryTempResult_ .isErr()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 594 | offset += result.Length(); |
| 595 | } |
| 596 | |
| 597 | MOZ_TRY_VAR(result, ctx.EndCompressing().mapErr(MapLZ4ErrorToNsresult))do { auto mozTryVarTempResult_ = (ctx.EndCompressing().mapErr (MapLZ4ErrorToNsresult)); if ((__builtin_expect(!!(mozTryVarTempResult_ .isErr()), 0))) { return mozTryVarTempResult_.propagateErr(); } (result) = mozTryVarTempResult_.unwrap(); } while (0); |
| 598 | MOZ_TRY(Write(fd, result.Elements(), result.Length()))do { auto mozTryTempResult_ = ::mozilla::ToResult(Write(fd, result .Elements(), result.Length())); if ((__builtin_expect(!!(mozTryTempResult_ .isErr()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 599 | offset += result.Length(); |
| 600 | value->mCompressedSize = offset - value->mOffset; |
| 601 | MOZ_TRY(Seek(fd, dataStart + offset))do { auto mozTryTempResult_ = ::mozilla::ToResult(Seek(fd, dataStart + offset)); if ((__builtin_expect(!!(mozTryTempResult_.isErr ()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 602 | } |
| 603 | |
| 604 | for (auto& e : entries) { |
| 605 | auto value = e.second; |
| 606 | uint8_t* headerEntry = buf.Get() + value->mHeaderOffsetInFile; |
| 607 | LittleEndian::writeUint32(headerEntry, value->mOffset); |
| 608 | LittleEndian::writeUint32(headerEntry + sizeof(value->mOffset), |
| 609 | value->mCompressedSize); |
| 610 | } |
| 611 | MOZ_TRY(Seek(fd, headerStart))do { auto mozTryTempResult_ = ::mozilla::ToResult(Seek(fd, headerStart )); if ((__builtin_expect(!!(mozTryTempResult_.isErr()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 612 | MOZ_TRY(Write(fd, buf.Get(), buf.cursor()))do { auto mozTryTempResult_ = ::mozilla::ToResult(Write(fd, buf .Get(), buf.cursor())); if ((__builtin_expect(!!(mozTryTempResult_ .isErr()), 0))) { return mozTryTempResult_.propagateErr(); } } while (0); |
| 613 | |
| 614 | mDirty = false; |
| 615 | mWrittenOnce = true; |
| 616 | |
| 617 | return Ok(); |
| 618 | } |
| 619 | |
| 620 | void StartupCache::InvalidateCache(bool memoryOnly) { |
| 621 | WaitOnPrefetch(); |
| 622 | // Ensure we're not writing using mTable... |
| 623 | MutexAutoLock lock(mTableLock); |
| 624 | |
| 625 | mWrittenOnce = false; |
| 626 | if (memoryOnly) { |
| 627 | // This should only be called in tests. |
| 628 | auto writeResult = WriteToDisk(); |
| 629 | if (NS_WARN_IF(writeResult.isErr())NS_warn_if_impl(writeResult.isErr(), "writeResult.isErr()", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 629)) { |
| 630 | gIgnoreDiskCache = true; |
| 631 | return; |
| 632 | } |
| 633 | } |
| 634 | if (mCurTableReferenced) { |
| 635 | // There should be no way for this assert to fail other than a user manually |
| 636 | // sending startupcache-invalidate messages through the Browser Toolbox. If |
| 637 | // something knowingly invalidates the cache, the event can be counted with |
| 638 | // mAllowedInvalidationsCount. |
| 639 | MOZ_DIAGNOSTIC_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType< decltype(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10)>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" " (" "Startup cache invalidated too many times." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 646); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" ") (" "Startup cache invalidated too many times." ")"); do { *((volatile int*)__null) = 646; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
| 640 | xpc::IsInAutomation() ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10)>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" " (" "Startup cache invalidated too many times." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 646); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" ") (" "Startup cache invalidated too many times." ")"); do { *((volatile int*)__null) = 646; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
| 641 | // The allowed invalidations can grow faster than the old tables, sodo { static_assert( mozilla::detail::AssertionConditionType< decltype(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10)>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" " (" "Startup cache invalidated too many times." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 646); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" ") (" "Startup cache invalidated too many times." ")"); do { *((volatile int*)__null) = 646; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
| 642 | // guard against incorrect unsigned subtraction.do { static_assert( mozilla::detail::AssertionConditionType< decltype(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10)>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" " (" "Startup cache invalidated too many times." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 646); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" ") (" "Startup cache invalidated too many times." ")"); do { *((volatile int*)__null) = 646; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
| 643 | mAllowedInvalidationsCount > mOldTables.Length() ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10)>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" " (" "Startup cache invalidated too many times." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 646); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" ") (" "Startup cache invalidated too many times." ")"); do { *((volatile int*)__null) = 646; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
| 644 | // Now perform the real check.do { static_assert( mozilla::detail::AssertionConditionType< decltype(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10)>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" " (" "Startup cache invalidated too many times." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 646); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" ") (" "Startup cache invalidated too many times." ")"); do { *((volatile int*)__null) = 646; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
| 645 | mOldTables.Length() - mAllowedInvalidationsCount < 10,do { static_assert( mozilla::detail::AssertionConditionType< decltype(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10)>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" " (" "Startup cache invalidated too many times." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 646); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" ") (" "Startup cache invalidated too many times." ")"); do { *((volatile int*)__null) = 646; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
| 646 | "Startup cache invalidated too many times.")do { static_assert( mozilla::detail::AssertionConditionType< decltype(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10)>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" " (" "Startup cache invalidated too many times." ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 646); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "xpc::IsInAutomation() || mAllowedInvalidationsCount > mOldTables.Length() || mOldTables.Length() - mAllowedInvalidationsCount < 10" ") (" "Startup cache invalidated too many times." ")"); do { *((volatile int*)__null) = 646; __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
| 647 | mOldTables.AppendElement(std::move(mTable)); |
| 648 | mCurTableReferenced = false; |
| 649 | } else { |
| 650 | mTable.clear(); |
| 651 | } |
| 652 | mRequestedCount = 0; |
| 653 | if (!memoryOnly) { |
| 654 | mCacheData.reset(); |
| 655 | nsresult rv = mFile->Remove(false); |
| 656 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) && rv != NS_ERROR_FILE_NOT_FOUND) { |
| 657 | gIgnoreDiskCache = true; |
| 658 | return; |
| 659 | } |
| 660 | } |
| 661 | gIgnoreDiskCache = false; |
| 662 | auto result = LoadArchive(); |
| 663 | if (NS_WARN_IF(result.isErr())NS_warn_if_impl(result.isErr(), "result.isErr()", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 663)) { |
| 664 | gIgnoreDiskCache = true; |
| 665 | } |
| 666 | } |
| 667 | |
| 668 | void StartupCache::CountAllowedInvalidation() { mAllowedInvalidationsCount++; } |
| 669 | |
| 670 | void StartupCache::MaybeInitShutdownWrite() { |
| 671 | if (mTimer) { |
| 672 | mTimer->Cancel(); |
| 673 | } |
| 674 | gShutdownInitiated = true; |
| 675 | |
| 676 | MaybeWriteOffMainThread(); |
| 677 | } |
| 678 | |
| 679 | void StartupCache::EnsureShutdownWriteComplete() { |
| 680 | MutexAutoLock lock(mTableLock); |
| 681 | // If we've already written or there's nothing to write, |
| 682 | // we don't need to do anything. This is the common case. |
| 683 | if (mWrittenOnce || (mCacheData.initialized() && !ShouldCompactCache())) { |
| 684 | return; |
| 685 | } |
| 686 | // Otherwise, ensure the write happens. The timer should have been cancelled |
| 687 | // already in MaybeInitShutdownWrite. |
| 688 | |
| 689 | // We got the lock. Keep the following in sync with |
| 690 | // MaybeWriteOffMainThread: |
| 691 | WaitOnPrefetch(); |
| 692 | mDirty = true; |
| 693 | mCacheData.reset(); |
| 694 | // Most of this should be redundant given MaybeWriteOffMainThread should |
| 695 | // have run before now. |
| 696 | |
| 697 | auto writeResult = WriteToDisk(); |
| 698 | Unused << NS_WARN_IF(writeResult.isErr())NS_warn_if_impl(writeResult.isErr(), "writeResult.isErr()", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 698); |
| 699 | // We've had the lock, and `WriteToDisk()` sets mWrittenOnce and mDirty |
| 700 | // when done, and checks for them when starting, so we don't need to do |
| 701 | // anything else. |
| 702 | } |
| 703 | |
| 704 | void StartupCache::IgnoreDiskCache() { |
| 705 | gIgnoreDiskCache = true; |
| 706 | if (gStartupCache) gStartupCache->InvalidateCache(); |
| 707 | } |
| 708 | |
| 709 | bool StartupCache::GetIgnoreDiskCache() { return gIgnoreDiskCache; } |
| 710 | |
| 711 | void StartupCache::WaitOnPrefetch() { |
| 712 | // This can't be called from within ThreadedPrefetch() |
| 713 | MonitorAutoLock lock(mPrefetchComplete); |
| 714 | while (mPrefetchInProgress) { |
| 715 | mPrefetchComplete.Wait(); |
| 716 | } |
| 717 | } |
| 718 | |
| 719 | void StartupCache::ThreadedPrefetch(uint8_t* aStart, size_t aSize) { |
| 720 | // Always notify of completion, even if MMAP_FAULT_HANDLER_CATCH() |
| 721 | // early-returns. |
| 722 | auto notifyPrefetchComplete = MakeScopeExit([&] { |
| 723 | MonitorAutoLock lock(mPrefetchComplete); |
| 724 | mPrefetchInProgress = false; |
| 725 | mPrefetchComplete.NotifyAll(); |
| 726 | }); |
| 727 | |
| 728 | // PrefetchMemory does madvise/equivalent, but doesn't access the memory |
| 729 | // pointed to by aStart |
| 730 | MMAP_FAULT_HANDLER_BEGIN_BUFFER(aStart, aSize){ MmapAccessScope mmapScope((void*)(aStart), (aSize)); if (__sigsetjmp (mmapScope.mJmpBuf, 0) == 0) { |
| 731 | PrefetchMemory(aStart, aSize); |
| 732 | MMAP_FAULT_HANDLER_CATCH()} else { NS_DebugBreak(NS_DEBUG_WARNING, "SIGBUS received when accessing mmapped file" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 732); return ; } } |
| 733 | } |
| 734 | |
| 735 | // mTableLock must be held |
| 736 | bool StartupCache::ShouldCompactCache() { |
| 737 | // If we've requested less than 4/5 of the startup cache, then we should |
| 738 | // probably compact it down. This can happen quite easily after the first run, |
| 739 | // which seems to request quite a few more things than subsequent runs. |
| 740 | CheckedInt<uint32_t> threshold = CheckedInt<uint32_t>(mTable.count()) * 4 / 5; |
| 741 | MOZ_RELEASE_ASSERT(threshold.isValid(), "Runaway StartupCache size")do { static_assert( mozilla::detail::AssertionConditionType< decltype(threshold.isValid())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(threshold.isValid()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("threshold.isValid()" " (" "Runaway StartupCache size" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 741); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "threshold.isValid()" ") (" "Runaway StartupCache size" ")"); do { *((volatile int *)__null) = 741; __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 742 | return mRequestedCount < threshold.value(); |
| 743 | } |
| 744 | |
| 745 | /* |
| 746 | * The write-thread is spawned on a timeout(which is reset with every write). |
| 747 | * This can avoid a slow shutdown. |
| 748 | */ |
| 749 | void StartupCache::WriteTimeout(nsITimer* aTimer, void* aClosure) { |
| 750 | /* |
| 751 | * It is safe to use the pointer passed in aClosure to reference the |
| 752 | * StartupCache object because the timer's lifetime is tightly coupled to |
| 753 | * the lifetime of the StartupCache object; this timer is canceled in the |
| 754 | * StartupCache destructor, guaranteeing that this function runs if and only |
| 755 | * if the StartupCache object is valid. |
| 756 | */ |
| 757 | StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure); |
| 758 | startupCacheObj->MaybeWriteOffMainThread(); |
| 759 | } |
| 760 | |
| 761 | /* |
| 762 | * See StartupCache::WriteTimeout above - this is just the non-static body. |
| 763 | */ |
| 764 | void StartupCache::MaybeWriteOffMainThread() { |
| 765 | { |
| 766 | MutexAutoLock lock(mTableLock); |
| 767 | if (mWrittenOnce || (mCacheData.initialized() && !ShouldCompactCache())) { |
| 768 | return; |
| 769 | } |
| 770 | } |
| 771 | // Keep this code in sync with EnsureShutdownWriteComplete. |
| 772 | WaitOnPrefetch(); |
| 773 | { |
| 774 | MutexAutoLock lock(mTableLock); |
| 775 | mDirty = true; |
| 776 | mCacheData.reset(); |
| 777 | } |
| 778 | |
| 779 | RefPtr<StartupCache> self = this; |
| 780 | nsCOMPtr<nsIRunnable> runnable = |
| 781 | NS_NewRunnableFunction("StartupCache::Write", [self]() mutable { |
| 782 | MutexAutoLock lock(self->mTableLock); |
| 783 | auto result = self->WriteToDisk(); |
| 784 | Unused << NS_WARN_IF(result.isErr())NS_warn_if_impl(result.isErr(), "result.isErr()", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 784); |
| 785 | }); |
| 786 | NS_DispatchBackgroundTask(runnable.forget(), NS_DISPATCH_EVENT_MAY_BLOCKnsIEventTarget::DISPATCH_EVENT_MAY_BLOCK); |
| 787 | } |
| 788 | |
| 789 | // We don't want to refcount StartupCache, so we'll just |
| 790 | // hold a ref to this and pass it to observerService instead. |
| 791 | NS_IMPL_ISUPPORTS(StartupCacheListener, nsIObserver)MozExternalRefCountType StartupCacheListener::AddRef(void) { static_assert (!std::is_destructible_v<StartupCacheListener>, "Reference-counted class " "StartupCacheListener" " should not have a public destructor. " "Make this class's destructor non-public"); do { static_assert ( mozilla::detail::AssertionConditionType<decltype(int32_t (mRefCnt) >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0" " (" "illegal refcnt" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0" ") (" "illegal refcnt" ")"); do { *((volatile int*)__null) = 791; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("StartupCacheListener" != nullptr)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !("StartupCacheListener" != nullptr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("\"StartupCacheListener\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"StartupCacheListener\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 791; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread .AssertOwnership("StartupCacheListener" " not thread-safe"); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this), (count), ("StartupCacheListener" ), (uint32_t)(sizeof(*this))); return count; } MozExternalRefCountType StartupCacheListener::Release(void) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(int32_t(mRefCnt) > 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(int32_t(mRefCnt) > 0))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0" " (" "dup release" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0" ") (" "dup release" ")"); do { *((volatile int*)__null) = 791 ; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("StartupCacheListener" != nullptr)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !("StartupCacheListener" != nullptr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("\"StartupCacheListener\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"StartupCacheListener\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 791; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread .AssertOwnership("StartupCacheListener" " not thread-safe"); const char* const nametmp = "StartupCacheListener"; nsrefcnt count = --mRefCnt; NS_LogRelease((this), (count), (nametmp)); if ( count == 0) { mRefCnt = 1; delete (this); return 0; } return count ; } nsresult StartupCacheListener::QueryInterface(const nsIID & aIID, void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!" , "aInstancePtr", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 791); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE ; static_assert(1 > 0, "Need more arguments to NS_INTERFACE_TABLE" ); static const QITableEntry table[] = { {&mozilla::detail ::kImplementedIID<StartupCacheListener, nsIObserver>, int32_t ( reinterpret_cast<char*>(static_cast<nsIObserver*> ((StartupCacheListener*)0x1000)) - reinterpret_cast<char*> ((StartupCacheListener*)0x1000))}, {&mozilla::detail::kImplementedIID <StartupCacheListener, nsISupports>, int32_t(reinterpret_cast <char*>(static_cast<nsISupports*>( static_cast< nsIObserver*>((StartupCacheListener*)0x1000))) - reinterpret_cast <char*>((StartupCacheListener*)0x1000))}, { nullptr, 0 } } ; static_assert((sizeof(table) / sizeof(table[0])) > 1, "need at least 1 interface"); rv = NS_TableDrivenQI(static_cast <void*>(this), aIID, aInstancePtr, table); return rv; } |
| 792 | |
| 793 | nsresult StartupCacheListener::Observe(nsISupports* subject, const char* topic, |
| 794 | const char16_t* data) { |
| 795 | StartupCache* sc = StartupCache::GetSingleton(); |
| 796 | if (!sc) return NS_OK; |
| 797 | |
| 798 | if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID"xpcom-shutdown") == 0) { |
| 799 | // Do not leave the thread running past xpcom shutdown |
| 800 | sc->WaitOnPrefetch(); |
| 801 | StartupCache::gShutdownInitiated = true; |
| 802 | // Note that we don't do anything special for the background write |
| 803 | // task; we expect the threadpool to finish running any tasks already |
| 804 | // posted to it prior to shutdown. FastShutdown will call |
| 805 | // EnsureShutdownWriteComplete() to ensure any pending writes happen |
| 806 | // in that case. |
| 807 | } else if (strcmp(topic, "startupcache-invalidate") == 0) { |
| 808 | sc->InvalidateCache(data && nsCRT::strcmp(data, u"memoryOnly") == 0); |
| 809 | } else if (strcmp(topic, "intl:app-locales-changed") == 0) { |
| 810 | // Live language switching invalidates the startup cache due to the history |
| 811 | // sidebar retaining localized strings in its internal SQL query. This |
| 812 | // should be a relatively rare event, but a user could do it an arbitrary |
| 813 | // number of times. |
| 814 | sc->CountAllowedInvalidation(); |
| 815 | } |
| 816 | return NS_OK; |
| 817 | } |
| 818 | |
| 819 | nsresult StartupCache::GetDebugObjectOutputStream( |
| 820 | nsIObjectOutputStream* aStream, nsIObjectOutputStream** aOutStream) { |
| 821 | NS_ENSURE_ARG_POINTER(aStream)do { if ((__builtin_expect(!!(!(aStream)), 0))) { NS_DebugBreak (NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aStream" ") failed", nullptr , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 821); return NS_ERROR_INVALID_POINTER; } } while (false); |
| 822 | #ifdef DEBUG1 |
| 823 | auto* stream = new StartupCacheDebugOutputStream(aStream, &mWriteObjectMap); |
| 824 | NS_ADDREF(*aOutStream = stream)(*aOutStream = stream)->AddRef(); |
| 825 | #else |
| 826 | NS_ADDREF(*aOutStream = aStream)(*aOutStream = aStream)->AddRef(); |
| 827 | #endif |
| 828 | |
| 829 | return NS_OK; |
| 830 | } |
| 831 | |
| 832 | nsresult StartupCache::ResetStartupWriteTimerCheckingReadCount() { |
| 833 | nsresult rv = NS_OK; |
| 834 | if (!mTimer) |
| 835 | mTimer = NS_NewTimer(); |
| 836 | else |
| 837 | rv = mTimer->Cancel(); |
| 838 | NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl (__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName (__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with " "result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t >(__rv), name ? " (" : "", name ? name : "", name ? ")" : "" ); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 838); return rv; } } while (false); |
| 839 | // Wait for the specified timeout, then write out the cache. |
| 840 | mTimer->InitWithNamedFuncCallback( |
| 841 | StartupCache::WriteTimeout, this, STARTUP_CACHE_WRITE_TIMEOUT * 1000, |
| 842 | nsITimer::TYPE_ONE_SHOT, "StartupCache::WriteTimeout"); |
| 843 | return NS_OK; |
| 844 | } |
| 845 | |
| 846 | // For test code only |
| 847 | nsresult StartupCache::ResetStartupWriteTimerAndLock() { |
| 848 | MutexAutoLock lock(mTableLock); |
| 849 | return ResetStartupWriteTimer(); |
| 850 | } |
| 851 | |
| 852 | nsresult StartupCache::ResetStartupWriteTimer() { |
| 853 | mDirty = true; |
| 854 | nsresult rv = NS_OK; |
| 855 | if (!mTimer) |
| 856 | mTimer = NS_NewTimer(); |
| 857 | else |
| 858 | rv = mTimer->Cancel(); |
| 859 | NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl (__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName (__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with " "result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t >(__rv), name ? " (" : "", name ? name : "", name ? ")" : "" ); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 859); return rv; } } while (false); |
| 860 | // Wait for the specified timeout, then write out the cache. |
| 861 | mTimer->InitWithNamedFuncCallback( |
| 862 | StartupCache::WriteTimeout, this, STARTUP_CACHE_WRITE_TIMEOUT * 1000, |
| 863 | nsITimer::TYPE_ONE_SHOT, "StartupCache::WriteTimeout"); |
| 864 | return NS_OK; |
| 865 | } |
| 866 | |
| 867 | // Used only in tests: |
| 868 | bool StartupCache::StartupWriteComplete() { |
| 869 | // Need to have written to disk and not added new things since; |
| 870 | MutexAutoLock lock(mTableLock); |
| 871 | return !mDirty && mWrittenOnce; |
| 872 | } |
| 873 | |
| 874 | // StartupCacheDebugOutputStream implementation |
| 875 | #ifdef DEBUG1 |
| 876 | NS_IMPL_ISUPPORTS(StartupCacheDebugOutputStream, nsIObjectOutputStream,MozExternalRefCountType StartupCacheDebugOutputStream::AddRef (void) { static_assert(!std::is_destructible_v<StartupCacheDebugOutputStream >, "Reference-counted class " "StartupCacheDebugOutputStream" " should not have a public destructor. " "Make this class's destructor non-public" ); do { static_assert( mozilla::detail::AssertionConditionType <decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0" " (" "illegal refcnt" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0" ") (" "illegal refcnt" ")"); do { *((volatile int*)__null) = 877; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("StartupCacheDebugOutputStream" != nullptr)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!("StartupCacheDebugOutputStream" != nullptr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"StartupCacheDebugOutputStream\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"StartupCacheDebugOutputStream\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 877; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread .AssertOwnership("StartupCacheDebugOutputStream" " not thread-safe" ); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this), (count), ( "StartupCacheDebugOutputStream"), (uint32_t)(sizeof(*this))); return count; } MozExternalRefCountType StartupCacheDebugOutputStream ::Release(void) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(int32_t(mRefCnt) > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0" " (" "dup release" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0" ") (" "dup release" ")"); do { *((volatile int*)__null) = 877 ; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("StartupCacheDebugOutputStream" != nullptr)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!("StartupCacheDebugOutputStream" != nullptr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"StartupCacheDebugOutputStream\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"StartupCacheDebugOutputStream\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 877; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread .AssertOwnership("StartupCacheDebugOutputStream" " not thread-safe" ); const char* const nametmp = "StartupCacheDebugOutputStream" ; nsrefcnt count = --mRefCnt; NS_LogRelease((this), (count), ( nametmp)); if (count == 0) { mRefCnt = 1; delete (this); return 0; } return count; } nsresult StartupCacheDebugOutputStream:: QueryInterface(const nsIID& aIID, void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!" , "aInstancePtr", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE ; static_assert(3 > 0, "Need more arguments to NS_INTERFACE_TABLE" ); static const QITableEntry table[] = { {&mozilla::detail ::kImplementedIID<StartupCacheDebugOutputStream, nsIObjectOutputStream >, int32_t( reinterpret_cast<char*>(static_cast<nsIObjectOutputStream *>((StartupCacheDebugOutputStream*)0x1000)) - reinterpret_cast <char*>((StartupCacheDebugOutputStream*)0x1000))}, {& mozilla::detail::kImplementedIID<StartupCacheDebugOutputStream , nsIBinaryOutputStream>, int32_t( reinterpret_cast<char *>(static_cast<nsIBinaryOutputStream*>((StartupCacheDebugOutputStream *)0x1000)) - reinterpret_cast<char*>((StartupCacheDebugOutputStream *)0x1000))}, {&mozilla::detail::kImplementedIID<StartupCacheDebugOutputStream , nsIOutputStream>, int32_t( reinterpret_cast<char*> (static_cast<nsIOutputStream*>((StartupCacheDebugOutputStream *)0x1000)) - reinterpret_cast<char*>((StartupCacheDebugOutputStream *)0x1000))}, {&mozilla::detail::kImplementedIID<StartupCacheDebugOutputStream , nsISupports>, int32_t(reinterpret_cast<char*>(static_cast <nsISupports*>( static_cast<nsIObjectOutputStream*> ((StartupCacheDebugOutputStream*)0x1000))) - reinterpret_cast <char*>((StartupCacheDebugOutputStream*)0x1000))}, { nullptr , 0 } } ; static_assert((sizeof(table) / sizeof(table[0])) > 1, "need at least 1 interface"); rv = NS_TableDrivenQI(static_cast <void*>(this), aIID, aInstancePtr, table); return rv; } |
| 877 | nsIBinaryOutputStream, nsIOutputStream)MozExternalRefCountType StartupCacheDebugOutputStream::AddRef (void) { static_assert(!std::is_destructible_v<StartupCacheDebugOutputStream >, "Reference-counted class " "StartupCacheDebugOutputStream" " should not have a public destructor. " "Make this class's destructor non-public" ); do { static_assert( mozilla::detail::AssertionConditionType <decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0" " (" "illegal refcnt" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0" ") (" "illegal refcnt" ")"); do { *((volatile int*)__null) = 877; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("StartupCacheDebugOutputStream" != nullptr)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!("StartupCacheDebugOutputStream" != nullptr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"StartupCacheDebugOutputStream\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"StartupCacheDebugOutputStream\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 877; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread .AssertOwnership("StartupCacheDebugOutputStream" " not thread-safe" ); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this), (count), ( "StartupCacheDebugOutputStream"), (uint32_t)(sizeof(*this))); return count; } MozExternalRefCountType StartupCacheDebugOutputStream ::Release(void) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(int32_t(mRefCnt) > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0" " (" "dup release" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0" ") (" "dup release" ")"); do { *((volatile int*)__null) = 877 ; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("StartupCacheDebugOutputStream" != nullptr)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!("StartupCacheDebugOutputStream" != nullptr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"StartupCacheDebugOutputStream\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"StartupCacheDebugOutputStream\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 877; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread .AssertOwnership("StartupCacheDebugOutputStream" " not thread-safe" ); const char* const nametmp = "StartupCacheDebugOutputStream" ; nsrefcnt count = --mRefCnt; NS_LogRelease((this), (count), ( nametmp)); if (count == 0) { mRefCnt = 1; delete (this); return 0; } return count; } nsresult StartupCacheDebugOutputStream:: QueryInterface(const nsIID& aIID, void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!" , "aInstancePtr", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 877); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE ; static_assert(3 > 0, "Need more arguments to NS_INTERFACE_TABLE" ); static const QITableEntry table[] = { {&mozilla::detail ::kImplementedIID<StartupCacheDebugOutputStream, nsIObjectOutputStream >, int32_t( reinterpret_cast<char*>(static_cast<nsIObjectOutputStream *>((StartupCacheDebugOutputStream*)0x1000)) - reinterpret_cast <char*>((StartupCacheDebugOutputStream*)0x1000))}, {& mozilla::detail::kImplementedIID<StartupCacheDebugOutputStream , nsIBinaryOutputStream>, int32_t( reinterpret_cast<char *>(static_cast<nsIBinaryOutputStream*>((StartupCacheDebugOutputStream *)0x1000)) - reinterpret_cast<char*>((StartupCacheDebugOutputStream *)0x1000))}, {&mozilla::detail::kImplementedIID<StartupCacheDebugOutputStream , nsIOutputStream>, int32_t( reinterpret_cast<char*> (static_cast<nsIOutputStream*>((StartupCacheDebugOutputStream *)0x1000)) - reinterpret_cast<char*>((StartupCacheDebugOutputStream *)0x1000))}, {&mozilla::detail::kImplementedIID<StartupCacheDebugOutputStream , nsISupports>, int32_t(reinterpret_cast<char*>(static_cast <nsISupports*>( static_cast<nsIObjectOutputStream*> ((StartupCacheDebugOutputStream*)0x1000))) - reinterpret_cast <char*>((StartupCacheDebugOutputStream*)0x1000))}, { nullptr , 0 } } ; static_assert((sizeof(table) / sizeof(table[0])) > 1, "need at least 1 interface"); rv = NS_TableDrivenQI(static_cast <void*>(this), aIID, aInstancePtr, table); return rv; } |
| 878 | |
| 879 | bool StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject) { |
| 880 | nsresult rv; |
| 881 | |
| 882 | nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject); |
| 883 | if (!classInfo) { |
| 884 | NS_ERROR("aObject must implement nsIClassInfo")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "aObject must implement nsIClassInfo" , "Error", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 884); MOZ_PretendNoReturn(); } while (0); |
| 885 | return false; |
| 886 | } |
| 887 | |
| 888 | uint32_t flags; |
| 889 | rv = classInfo->GetFlags(&flags); |
| 890 | NS_ENSURE_SUCCESS(rv, false)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl (__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName (__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with " "result 0x%" "X" "%s%s%s", "rv", "false", static_cast<uint32_t >(__rv), name ? " (" : "", name ? name : "", name ? ")" : "" ); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 890); return false; } } while (false); |
| 891 | if (flags & nsIClassInfo::SINGLETON) return true; |
| 892 | |
| 893 | bool inserted = mObjectMap->EnsureInserted(aObject); |
| 894 | if (!inserted) { |
| 895 | NS_ERROR(do { NS_DebugBreak(NS_DEBUG_ASSERTION, "non-singleton aObject is referenced multiple times in this" "serialization, we don't support that.", "Error", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 897); MOZ_PretendNoReturn(); } while (0) |
| 896 | "non-singleton aObject is referenced multiple times in this"do { NS_DebugBreak(NS_DEBUG_ASSERTION, "non-singleton aObject is referenced multiple times in this" "serialization, we don't support that.", "Error", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 897); MOZ_PretendNoReturn(); } while (0) |
| 897 | "serialization, we don't support that.")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "non-singleton aObject is referenced multiple times in this" "serialization, we don't support that.", "Error", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 897); MOZ_PretendNoReturn(); } while (0); |
| 898 | } |
| 899 | |
| 900 | return inserted; |
| 901 | } |
| 902 | |
| 903 | // nsIObjectOutputStream implementation |
| 904 | nsresult StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, |
| 905 | bool aIsStrongRef) { |
| 906 | nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject)); |
| 907 | |
| 908 | NS_ASSERTION(rootObject.get() == aObject,do { if (!(rootObject.get() == aObject)) { NS_DebugBreak(NS_DEBUG_ASSERTION , "bad call to WriteObject -- call WriteCompoundObject!", "rootObject.get() == aObject" , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 909); MOZ_PretendNoReturn(); } } while (0) |
| 909 | "bad call to WriteObject -- call WriteCompoundObject!")do { if (!(rootObject.get() == aObject)) { NS_DebugBreak(NS_DEBUG_ASSERTION , "bad call to WriteObject -- call WriteCompoundObject!", "rootObject.get() == aObject" , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 909); MOZ_PretendNoReturn(); } } while (0); |
| 910 | bool check = CheckReferences(aObject); |
| 911 | NS_ENSURE_TRUE(check, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(check)), 0))) { NS_DebugBreak (NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "check" ") failed", nullptr , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 911); return NS_ERROR_FAILURE; } } while (false); |
| 912 | return mBinaryStream->WriteObject(aObject, aIsStrongRef); |
| 913 | } |
| 914 | |
| 915 | nsresult StartupCacheDebugOutputStream::WriteSingleRefObject( |
| 916 | nsISupports* aObject) { |
| 917 | nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject)); |
| 918 | |
| 919 | NS_ASSERTION(rootObject.get() == aObject,do { if (!(rootObject.get() == aObject)) { NS_DebugBreak(NS_DEBUG_ASSERTION , "bad call to WriteSingleRefObject -- call WriteCompoundObject!" , "rootObject.get() == aObject", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 920); MOZ_PretendNoReturn(); } } while (0) |
| 920 | "bad call to WriteSingleRefObject -- call WriteCompoundObject!")do { if (!(rootObject.get() == aObject)) { NS_DebugBreak(NS_DEBUG_ASSERTION , "bad call to WriteSingleRefObject -- call WriteCompoundObject!" , "rootObject.get() == aObject", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 920); MOZ_PretendNoReturn(); } } while (0); |
| 921 | bool check = CheckReferences(aObject); |
| 922 | NS_ENSURE_TRUE(check, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(check)), 0))) { NS_DebugBreak (NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "check" ") failed", nullptr , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 922); return NS_ERROR_FAILURE; } } while (false); |
| 923 | return mBinaryStream->WriteSingleRefObject(aObject); |
| 924 | } |
| 925 | |
| 926 | nsresult StartupCacheDebugOutputStream::WriteCompoundObject( |
| 927 | nsISupports* aObject, const nsIID& aIID, bool aIsStrongRef) { |
| 928 | nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject)); |
| 929 | |
| 930 | nsCOMPtr<nsISupports> roundtrip; |
| 931 | rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip)); |
| 932 | NS_ASSERTION(roundtrip.get() == aObject,do { if (!(roundtrip.get() == aObject)) { NS_DebugBreak(NS_DEBUG_ASSERTION , "bad aggregation or multiple inheritance detected by call to " "WriteCompoundObject!", "roundtrip.get() == aObject", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 934); MOZ_PretendNoReturn(); } } while (0) |
| 933 | "bad aggregation or multiple inheritance detected by call to "do { if (!(roundtrip.get() == aObject)) { NS_DebugBreak(NS_DEBUG_ASSERTION , "bad aggregation or multiple inheritance detected by call to " "WriteCompoundObject!", "roundtrip.get() == aObject", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 934); MOZ_PretendNoReturn(); } } while (0) |
| 934 | "WriteCompoundObject!")do { if (!(roundtrip.get() == aObject)) { NS_DebugBreak(NS_DEBUG_ASSERTION , "bad aggregation or multiple inheritance detected by call to " "WriteCompoundObject!", "roundtrip.get() == aObject", "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 934); MOZ_PretendNoReturn(); } } while (0); |
| 935 | |
| 936 | bool check = CheckReferences(aObject); |
| 937 | NS_ENSURE_TRUE(check, NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!(check)), 0))) { NS_DebugBreak (NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "check" ") failed", nullptr , "/var/lib/jenkins/workspace/firefox-scan-build/startupcache/StartupCache.cpp" , 937); return NS_ERROR_FAILURE; } } while (false); |
| 938 | return mBinaryStream->WriteCompoundObject(aObject, aIID, aIsStrongRef); |
| 939 | } |
| 940 | |
| 941 | nsresult StartupCacheDebugOutputStream::WriteID(nsID const& aID) { |
| 942 | return mBinaryStream->WriteID(aID); |
| 943 | } |
| 944 | |
| 945 | char* StartupCacheDebugOutputStream::GetBuffer(uint32_t aLength, |
| 946 | uint32_t aAlignMask) { |
| 947 | return mBinaryStream->GetBuffer(aLength, aAlignMask); |
| 948 | } |
| 949 | |
| 950 | void StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, uint32_t aLength) { |
| 951 | mBinaryStream->PutBuffer(aBuffer, aLength); |
| 952 | } |
| 953 | #endif // DEBUG |
| 954 | |
| 955 | } // namespace scache |
| 956 | } // namespace mozilla |