Bug 1371882 - MEMORYBLOCKCACHE_ERRORS telemetry to catch unexpected errors without crashing - r?cpearce
No errors are expected to happen in MemoryBlockCache (except a few
'InitAllocation', which would still be good to know about), but instead of
taking drastic measures in these cases (i.e., crash), I would prefer to
collect some telemetry first.
MozReview-Commit-ID: 4WdFS34lgzj
--- a/dom/media/MemoryBlockCache.cpp
+++ b/dom/media/MemoryBlockCache.cpp
@@ -10,16 +10,30 @@
namespace mozilla {
#undef LOG
LazyLogModule gMemoryBlockCacheLog("MemoryBlockCache");
#define LOG(x, ...) \
MOZ_LOG(gMemoryBlockCacheLog, LogLevel::Debug, ("%p " x, this, ##__VA_ARGS__))
+enum MemoryBlockCacheTelemetryErrors
+{
+ // Don't change order/numbers! Add new values at the end and update
+ // MEMORYBLOCKCACHE_ERRORS description in Histograms.json.
+ InitUnderuse = 0,
+ InitAllocation = 1,
+ ReadClosed = 2,
+ ReadOverflow = 3,
+ WriteBlockClosed = 4,
+ WriteBlockOverflow = 5,
+ MoveBlockClosed = 6,
+ MoveBlockOverflow = 7,
+};
+
MemoryBlockCache::MemoryBlockCache(int64_t aContentLength)
// Buffer whole blocks.
: mBufferLength(size_t(((aContentLength - 1) / BLOCK_SIZE + 1) * BLOCK_SIZE))
, mMutex("MemoryBlockCache")
{
MOZ_ASSERT(aContentLength > 0);
}
@@ -29,23 +43,33 @@ MemoryBlockCache::~MemoryBlockCache()
}
nsresult
MemoryBlockCache::Init()
{
LOG("Init()");
if (mBufferLength <= 0) {
+ LOG("MemoryBlockCache::WriteBlock(this=%p) MEMORYBLOCKCACHE_ERRORS='InitUnderuse'",
+ this);
+ Telemetry::Accumulate(Telemetry::HistogramID::MEMORYBLOCKCACHE_ERRORS,
+ InitUnderuse);
return NS_ERROR_FAILURE;
}
MutexAutoLock lock(mMutex);
- return mBuffer.SetLength(mBufferLength, mozilla::fallible) ? NS_OK
- : NS_ERROR_FAILURE;
+ if (!mBuffer.SetLength(mBufferLength, mozilla::fallible)) {
+ LOG("MemoryBlockCache::Init(this=%p) MEMORYBLOCKCACHE_ERRORS='InitAllocation'",
+ this);
+ Telemetry::Accumulate(Telemetry::HistogramID::MEMORYBLOCKCACHE_ERRORS,
+ InitAllocation);
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
}
void
MemoryBlockCache::Close()
{
LOG("Close()");
MutexAutoLock lock(mMutex);
mBuffer.SetLength(0);
@@ -56,16 +80,20 @@ MemoryBlockCache::WriteBlock(uint32_t aB
Span<const uint8_t> aData1,
Span<const uint8_t> aData2)
{
MutexAutoLock lock(mMutex);
size_t offset = BlockIndexToOffset(aBlockIndex);
if (offset + aData1.Length() + aData2.Length() > mBuffer.Length()) {
// Closed, or trying to write over the expected limit.
+ LOG("MemoryBlockCache::WriteBlock(this=%p) MEMORYBLOCKCACHE_ERRORS='%s'",
+ this, mBuffer.IsEmpty() ? "WriteBlockClosed" : "WriteBlockOverflow");
+ Telemetry::Accumulate(Telemetry::HistogramID::MEMORYBLOCKCACHE_ERRORS,
+ mBuffer.IsEmpty() ? WriteBlockClosed : WriteBlockOverflow);
return NS_ERROR_FAILURE;
}
memcpy(mBuffer.Elements() + offset, aData1.Elements(), aData1.Length());
if (aData2.Length() > 0) {
memcpy(mBuffer.Elements() + offset + aData1.Length(),
aData2.Elements(),
aData2.Length());
@@ -80,16 +108,20 @@ MemoryBlockCache::Read(int64_t aOffset,
int32_t aLength,
int32_t* aBytes)
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(aOffset >= 0);
if (aOffset + aLength > int64_t(mBuffer.Length())) {
// Closed, or trying to write over the expected limit.
+ LOG("MemoryBlockCache::Read(this=%p) MEMORYBLOCKCACHE_ERRORS='%s'",
+ this, mBuffer.IsEmpty() ? "ReadClosed" : "ReadOverflow");
+ Telemetry::Accumulate(Telemetry::HistogramID::MEMORYBLOCKCACHE_ERRORS,
+ mBuffer.IsEmpty() ? ReadClosed : ReadOverflow);
return NS_ERROR_FAILURE;
}
memcpy(aData, mBuffer.Elements() + aOffset, aLength);
*aBytes = aLength;
return NS_OK;
}
@@ -99,16 +131,20 @@ MemoryBlockCache::MoveBlock(int32_t aSou
{
MutexAutoLock lock(mMutex);
size_t sourceOffset = BlockIndexToOffset(aSourceBlockIndex);
size_t destOffset = BlockIndexToOffset(aDestBlockIndex);
if (sourceOffset + BLOCK_SIZE > mBuffer.Length() ||
destOffset + BLOCK_SIZE > mBuffer.Length()) {
// Closed, or trying to reach past the buffer.
+ LOG("MemoryBlockCache::MoveBlock(this=%p) MEMORYBLOCKCACHE_ERRORS='%s'",
+ this, mBuffer.IsEmpty() ? "MoveBlockClosed" : "MoveBlockOverflow");
+ Telemetry::Accumulate(Telemetry::HistogramID::MEMORYBLOCKCACHE_ERRORS,
+ mBuffer.IsEmpty() ? MoveBlockClosed : MoveBlockOverflow);
return NS_ERROR_FAILURE;
}
memcpy(mBuffer.Elements() + destOffset,
mBuffer.Elements() + sourceOffset,
BLOCK_SIZE);
return NS_OK;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8447,16 +8447,25 @@
"alert_emails": ["gsquelart@mozilla.com"],
"bug_numbers": [1371205],
"expires_in_version": "60",
"kind": "linear",
"high": 16777216,
"n_buckets": 66,
"description": "MediaCacheStream stream notified length size in bytes, from the HTTP header. Recorded when each MediaCacheStream is first notified."
},
+ "MEMORYBLOCKCACHE_ERRORS": {
+ "record_in_processes": ["main", "content"],
+ "alert_emails": ["gsquelart@mozilla.com"],
+ "bug_numbers": [1371882],
+ "expires_in_version": "60",
+ "kind": "enumerated",
+ "n_values": 16,
+ "description": "Unexpected errors encountered during use of MemoryBlockCache. 0=InitUnderuse, 1=InitAllocation, 2=ReadClosed, 3=ReadOverflow, 4=WriteBlockClosed, 5=WriteBlockOverflow, 6=MoveBlockClosed, 7=MoveBlockOverflow"
+ },
"VIDEO_MFT_OUTPUT_NULL_SAMPLES": {
"record_in_processes": ["main", "content"],
"alert_emails": ["cpearce@mozilla.com"],
"expires_in_version": "53",
"kind": "enumerated",
"n_values": 10,
"description": "Does the WMF video decoder return success but null output? 0 = playback successful, 1 = excessive null output but able to decode some frames, 2 = excessive null output and gave up, 3 = null output but recovered, 4 = non-excessive null output without being able to decode frames.",
"bug_numbers": [1176071]