- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 0 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 91 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 91 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 92 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
#include "mozilla/Attributes.h"
#include "mozilla/BaseAndGeckoProfilerDetail.h"
#include "mozilla/BaseProfileJSONWriter.h"
#include "mozilla/BaseProfiler.h"
#include "mozilla/BaseProfilerDetail.h"
#include "mozilla/FailureLatch.h"
#include "mozilla/NotNull.h"
#include "mozilla/ProgressLogger.h"
#include "mozilla/ProportionValue.h"
#include "mozilla/BaseProfilerMarkerTypes.h"
#include "mozilla/leb128iterator.h"
#include "mozilla/ModuloBuffer.h"
#include "mozilla/mozalloc.h"
#include "mozilla/PowerOfTwo.h"
#include "mozilla/ProfileBufferChunk.h"
#include "mozilla/ProfileBufferChunkManagerSingle.h"
#include "mozilla/ProfileBufferChunkManagerWithLocalLimit.h"
#include "mozilla/ProfileBufferControlledChunkManager.h"
#include "mozilla/ProfileChunkedBuffer.h"
#include "mozilla/Vector.h"
#if defined(_MSC_VER) || defined(__MINGW32__)
# include <windows.h>
# include <mmsystem.h>
# include <process.h>
#else
# include <errno.h>
# include <time.h>
#endif
#include <algorithm>
#include <atomic>
#include <iostream>
#include <random>
#include <thread>
#include <type_traits>
#include <utility>
void TestFailureLatch() {
printf("TestFailureLatch...\n");
// Test infallible latch.
{
mozilla::FailureLatchInfallibleSource& infallibleLatch =
mozilla::FailureLatchInfallibleSource::Singleton();
MOZ_RELEASE_ASSERT(!infallibleLatch.Fallible());
MOZ_RELEASE_ASSERT(!infallibleLatch.Failed());
MOZ_RELEASE_ASSERT(!infallibleLatch.GetFailure());
MOZ_RELEASE_ASSERT(&infallibleLatch.SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
MOZ_RELEASE_ASSERT(&std::as_const(infallibleLatch).SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
}
// Test failure latch basic functions.
{
mozilla::FailureLatchSource failureLatch;
MOZ_RELEASE_ASSERT(failureLatch.Fallible());
MOZ_RELEASE_ASSERT(!failureLatch.Failed());
MOZ_RELEASE_ASSERT(!failureLatch.GetFailure());
MOZ_RELEASE_ASSERT(&failureLatch.SourceFailureLatch() == &failureLatch);
MOZ_RELEASE_ASSERT(&std::as_const(failureLatch).SourceFailureLatch() ==
&failureLatch);
failureLatch.SetFailure("error");
MOZ_RELEASE_ASSERT(failureLatch.Fallible());
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(failureLatch.GetFailure());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
failureLatch.SetFailure("later error");
MOZ_RELEASE_ASSERT(failureLatch.Fallible());
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(failureLatch.GetFailure());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
}
// Test SetFailureFrom.
{
mozilla::FailureLatchSource failureLatch;
MOZ_RELEASE_ASSERT(!failureLatch.Failed());
failureLatch.SetFailureFrom(failureLatch);
MOZ_RELEASE_ASSERT(!failureLatch.Failed());
MOZ_RELEASE_ASSERT(!failureLatch.GetFailure());
// SetFailureFrom with no error.
{
mozilla::FailureLatchSource failureLatchInnerOk;
MOZ_RELEASE_ASSERT(!failureLatchInnerOk.Failed());
MOZ_RELEASE_ASSERT(!failureLatchInnerOk.GetFailure());
MOZ_RELEASE_ASSERT(!failureLatch.Failed());
failureLatch.SetFailureFrom(failureLatchInnerOk);
MOZ_RELEASE_ASSERT(!failureLatch.Failed());
MOZ_RELEASE_ASSERT(!failureLatchInnerOk.Failed());
MOZ_RELEASE_ASSERT(!failureLatchInnerOk.GetFailure());
}
MOZ_RELEASE_ASSERT(!failureLatch.Failed());
MOZ_RELEASE_ASSERT(!failureLatch.GetFailure());
// SetFailureFrom with error.
{
mozilla::FailureLatchSource failureLatchInnerError;
MOZ_RELEASE_ASSERT(!failureLatchInnerError.Failed());
MOZ_RELEASE_ASSERT(!failureLatchInnerError.GetFailure());
failureLatchInnerError.SetFailure("inner error");
MOZ_RELEASE_ASSERT(failureLatchInnerError.Failed());
MOZ_RELEASE_ASSERT(
strcmp(failureLatchInnerError.GetFailure(), "inner error") == 0);
MOZ_RELEASE_ASSERT(!failureLatch.Failed());
failureLatch.SetFailureFrom(failureLatchInnerError);
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(failureLatchInnerError.Failed());
MOZ_RELEASE_ASSERT(
strcmp(failureLatchInnerError.GetFailure(), "inner error") == 0);
}
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "inner error") == 0);
failureLatch.SetFailureFrom(failureLatch);
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "inner error") == 0);
// SetFailureFrom with error again, ignored.
{
mozilla::FailureLatchSource failureLatchInnerError;
failureLatchInnerError.SetFailure("later inner error");
MOZ_RELEASE_ASSERT(failureLatchInnerError.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatchInnerError.GetFailure(),
"later inner error") == 0);
MOZ_RELEASE_ASSERT(failureLatch.Failed());
failureLatch.SetFailureFrom(failureLatchInnerError);
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(failureLatchInnerError.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatchInnerError.GetFailure(),
"later inner error") == 0);
}
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "inner error") == 0);
}
// Test FAILURELATCH_IMPL_PROXY
{
class Proxy final : public mozilla::FailureLatch {
public:
explicit Proxy(mozilla::FailureLatch& aFailureLatch)
: mFailureLatch(WrapNotNull(&aFailureLatch)) {}
void Set(mozilla::FailureLatch& aFailureLatch) {
mFailureLatch = WrapNotNull(&aFailureLatch);
}
FAILURELATCH_IMPL_PROXY(*mFailureLatch)
private:
mozilla::NotNull<mozilla::FailureLatch*> mFailureLatch;
};
Proxy proxy{mozilla::FailureLatchInfallibleSource::Singleton()};
MOZ_RELEASE_ASSERT(!proxy.Fallible());
MOZ_RELEASE_ASSERT(!proxy.Failed());
MOZ_RELEASE_ASSERT(!proxy.GetFailure());
MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
// Error from proxy.
{
mozilla::FailureLatchSource failureLatch;
proxy.Set(failureLatch);
MOZ_RELEASE_ASSERT(proxy.Fallible());
MOZ_RELEASE_ASSERT(!proxy.Failed());
MOZ_RELEASE_ASSERT(!proxy.GetFailure());
MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() == &failureLatch);
MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
&failureLatch);
proxy.SetFailure("error");
MOZ_RELEASE_ASSERT(proxy.Failed());
MOZ_RELEASE_ASSERT(strcmp(proxy.GetFailure(), "error") == 0);
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
// Don't forget to stop pointing at soon-to-be-destroyed object.
proxy.Set(mozilla::FailureLatchInfallibleSource::Singleton());
}
// Error from proxy's origin.
{
mozilla::FailureLatchSource failureLatch;
proxy.Set(failureLatch);
MOZ_RELEASE_ASSERT(proxy.Fallible());
MOZ_RELEASE_ASSERT(!proxy.Failed());
MOZ_RELEASE_ASSERT(!proxy.GetFailure());
MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() == &failureLatch);
MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
&failureLatch);
failureLatch.SetFailure("error");
MOZ_RELEASE_ASSERT(proxy.Failed());
MOZ_RELEASE_ASSERT(strcmp(proxy.GetFailure(), "error") == 0);
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
// Don't forget to stop pointing at soon-to-be-destroyed object.
proxy.Set(mozilla::FailureLatchInfallibleSource::Singleton());
}
MOZ_RELEASE_ASSERT(!proxy.Fallible());
MOZ_RELEASE_ASSERT(!proxy.Failed());
MOZ_RELEASE_ASSERT(!proxy.GetFailure());
MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
}
// Test FAILURELATCH_IMPL_PROXY_OR_INFALLIBLE
{
class ProxyOrNull final : public mozilla::FailureLatch {
public:
ProxyOrNull() = default;
void Set(mozilla::FailureLatch* aFailureLatchOrNull) {
mFailureLatchOrNull = aFailureLatchOrNull;
}
FAILURELATCH_IMPL_PROXY_OR_INFALLIBLE(mFailureLatchOrNull, ProxyOrNull)
private:
mozilla::FailureLatch* mFailureLatchOrNull = nullptr;
};
ProxyOrNull proxy;
MOZ_RELEASE_ASSERT(!proxy.Fallible());
MOZ_RELEASE_ASSERT(!proxy.Failed());
MOZ_RELEASE_ASSERT(!proxy.GetFailure());
MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
// Error from proxy.
{
mozilla::FailureLatchSource failureLatch;
proxy.Set(&failureLatch);
MOZ_RELEASE_ASSERT(proxy.Fallible());
MOZ_RELEASE_ASSERT(!proxy.Failed());
MOZ_RELEASE_ASSERT(!proxy.GetFailure());
MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() == &failureLatch);
MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
&failureLatch);
proxy.SetFailure("error");
MOZ_RELEASE_ASSERT(proxy.Failed());
MOZ_RELEASE_ASSERT(strcmp(proxy.GetFailure(), "error") == 0);
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
// Don't forget to stop pointing at soon-to-be-destroyed object.
proxy.Set(nullptr);
}
// Error from proxy's origin.
{
mozilla::FailureLatchSource failureLatch;
proxy.Set(&failureLatch);
MOZ_RELEASE_ASSERT(proxy.Fallible());
MOZ_RELEASE_ASSERT(!proxy.Failed());
MOZ_RELEASE_ASSERT(!proxy.GetFailure());
MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() == &failureLatch);
MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
&failureLatch);
failureLatch.SetFailure("error");
MOZ_RELEASE_ASSERT(proxy.Failed());
MOZ_RELEASE_ASSERT(strcmp(proxy.GetFailure(), "error") == 0);
MOZ_RELEASE_ASSERT(failureLatch.Failed());
MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
// Don't forget to stop pointing at soon-to-be-destroyed object.
proxy.Set(nullptr);
}
MOZ_RELEASE_ASSERT(!proxy.Fallible());
MOZ_RELEASE_ASSERT(!proxy.Failed());
MOZ_RELEASE_ASSERT(!proxy.GetFailure());
MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
&mozilla::FailureLatchInfallibleSource::Singleton());
}
printf("TestFailureLatch done\n");
}
void TestProfilerUtils() {
printf("TestProfilerUtils...\n");
{
using mozilla::baseprofiler::BaseProfilerProcessId;
using Number = BaseProfilerProcessId::NumberType;
static constexpr Number scMaxNumber = std::numeric_limits<Number>::max();
static_assert(
BaseProfilerProcessId{}.ToNumber() == 0,
"These tests assume that the unspecified process id number is 0; "
"if this fails, please update these tests accordingly");
static_assert(!BaseProfilerProcessId{}.IsSpecified());
static_assert(!BaseProfilerProcessId::FromNumber(0).IsSpecified());
static_assert(BaseProfilerProcessId::FromNumber(1).IsSpecified());
static_assert(BaseProfilerProcessId::FromNumber(123).IsSpecified());
static_assert(BaseProfilerProcessId::FromNumber(scMaxNumber).IsSpecified());
static_assert(BaseProfilerProcessId::FromNumber(Number(1)).ToNumber() ==
Number(1));
static_assert(BaseProfilerProcessId::FromNumber(Number(123)).ToNumber() ==
Number(123));
static_assert(BaseProfilerProcessId::FromNumber(scMaxNumber).ToNumber() ==
scMaxNumber);
static_assert(BaseProfilerProcessId{} == BaseProfilerProcessId{});
static_assert(BaseProfilerProcessId::FromNumber(Number(123)) ==
BaseProfilerProcessId::FromNumber(Number(123)));
static_assert(BaseProfilerProcessId{} !=
BaseProfilerProcessId::FromNumber(Number(123)));
static_assert(BaseProfilerProcessId::FromNumber(Number(123)) !=
BaseProfilerProcessId{});
static_assert(BaseProfilerProcessId::FromNumber(Number(123)) !=
BaseProfilerProcessId::FromNumber(scMaxNumber));
static_assert(BaseProfilerProcessId::FromNumber(scMaxNumber) !=
BaseProfilerProcessId::FromNumber(Number(123)));
// Verify trivial-copyability by memcpy'ing to&from same-size storage.
static_assert(std::is_trivially_copyable_v<BaseProfilerProcessId>);
BaseProfilerProcessId pid;
MOZ_RELEASE_ASSERT(!pid.IsSpecified());
Number pidStorage;
static_assert(sizeof(pidStorage) == sizeof(pid));
// Copy from BaseProfilerProcessId to storage. Note: We cannot assume that
// this is equal to what ToNumber() gives us. All we can do is verify that
// copying from storage back to BaseProfilerProcessId works as expected.
std::memcpy(&pidStorage, &pid, sizeof(pidStorage));
BaseProfilerProcessId pid2 = BaseProfilerProcessId::FromNumber(2);
MOZ_RELEASE_ASSERT(pid2.IsSpecified());
std::memcpy(&pid2, &pidStorage, sizeof(pid));
MOZ_RELEASE_ASSERT(!pid2.IsSpecified());
pid = BaseProfilerProcessId::FromNumber(123);
std::memcpy(&pidStorage, &pid, sizeof(pidStorage));
pid2 = BaseProfilerProcessId{};
MOZ_RELEASE_ASSERT(!pid2.IsSpecified());
std::memcpy(&pid2, &pidStorage, sizeof(pid));
MOZ_RELEASE_ASSERT(pid2.IsSpecified());
MOZ_RELEASE_ASSERT(pid2.ToNumber() == 123);
// No conversions to/from numbers.
static_assert(!std::is_constructible_v<BaseProfilerProcessId, Number>);
static_assert(!std::is_assignable_v<BaseProfilerProcessId, Number>);
static_assert(!std::is_constructible_v<Number, BaseProfilerProcessId>);
static_assert(!std::is_assignable_v<Number, BaseProfilerProcessId>);
static_assert(
std::is_same_v<
decltype(mozilla::baseprofiler::profiler_current_process_id()),
BaseProfilerProcessId>);
MOZ_RELEASE_ASSERT(
mozilla::baseprofiler::profiler_current_process_id().IsSpecified());
}
{
mozilla::baseprofiler::profiler_init_main_thread_id();
using mozilla::baseprofiler::BaseProfilerThreadId;
using Number = BaseProfilerThreadId::NumberType;
static constexpr Number scMaxNumber = std::numeric_limits<Number>::max();
static_assert(
BaseProfilerThreadId{}.ToNumber() == 0,
"These tests assume that the unspecified thread id number is 0; "
"if this fails, please update these tests accordingly");
static_assert(!BaseProfilerThreadId{}.IsSpecified());
static_assert(!BaseProfilerThreadId::FromNumber(0).IsSpecified());
static_assert(BaseProfilerThreadId::FromNumber(1).IsSpecified());
static_assert(BaseProfilerThreadId::FromNumber(123).IsSpecified());
static_assert(BaseProfilerThreadId::FromNumber(scMaxNumber).IsSpecified());
static_assert(BaseProfilerThreadId::FromNumber(Number(1)).ToNumber() ==
Number(1));
static_assert(BaseProfilerThreadId::FromNumber(Number(123)).ToNumber() ==
Number(123));
static_assert(BaseProfilerThreadId::FromNumber(scMaxNumber).ToNumber() ==
scMaxNumber);
static_assert(BaseProfilerThreadId{} == BaseProfilerThreadId{});
static_assert(BaseProfilerThreadId::FromNumber(Number(123)) ==
BaseProfilerThreadId::FromNumber(Number(123)));
static_assert(BaseProfilerThreadId{} !=
BaseProfilerThreadId::FromNumber(Number(123)));
static_assert(BaseProfilerThreadId::FromNumber(Number(123)) !=
BaseProfilerThreadId{});
static_assert(BaseProfilerThreadId::FromNumber(Number(123)) !=
BaseProfilerThreadId::FromNumber(scMaxNumber));
static_assert(BaseProfilerThreadId::FromNumber(scMaxNumber) !=
BaseProfilerThreadId::FromNumber(Number(123)));
// Verify trivial-copyability by memcpy'ing to&from same-size storage.
static_assert(std::is_trivially_copyable_v<BaseProfilerThreadId>);
BaseProfilerThreadId tid;
MOZ_RELEASE_ASSERT(!tid.IsSpecified());
Number tidStorage;
static_assert(sizeof(tidStorage) == sizeof(tid));
// Copy from BaseProfilerThreadId to storage. Note: We cannot assume that
// this is equal to what ToNumber() gives us. All we can do is verify that
// copying from storage back to BaseProfilerThreadId works as expected.
std::memcpy(&tidStorage, &tid, sizeof(tidStorage));
BaseProfilerThreadId tid2 = BaseProfilerThreadId::FromNumber(2);
MOZ_RELEASE_ASSERT(tid2.IsSpecified());
std::memcpy(&tid2, &tidStorage, sizeof(tid));
MOZ_RELEASE_ASSERT(!tid2.IsSpecified());
tid = BaseProfilerThreadId::FromNumber(Number(123));
std::memcpy(&tidStorage, &tid, sizeof(tidStorage));
tid2 = BaseProfilerThreadId{};
MOZ_RELEASE_ASSERT(!tid2.IsSpecified());
std::memcpy(&tid2, &tidStorage, sizeof(tid));
MOZ_RELEASE_ASSERT(tid2.IsSpecified());
MOZ_RELEASE_ASSERT(tid2.ToNumber() == Number(123));
// No conversions to/from numbers.
static_assert(!std::is_constructible_v<BaseProfilerThreadId, Number>);
static_assert(!std::is_assignable_v<BaseProfilerThreadId, Number>);
static_assert(!std::is_constructible_v<Number, BaseProfilerThreadId>);
static_assert(!std::is_assignable_v<Number, BaseProfilerThreadId>);
static_assert(std::is_same_v<
decltype(mozilla::baseprofiler::profiler_current_thread_id()),
BaseProfilerThreadId>);
BaseProfilerThreadId mainTestThreadId =
mozilla::baseprofiler::profiler_current_thread_id();
MOZ_RELEASE_ASSERT(mainTestThreadId.IsSpecified());
BaseProfilerThreadId mainThreadId =
mozilla::baseprofiler::profiler_main_thread_id();
MOZ_RELEASE_ASSERT(mainThreadId.IsSpecified());
MOZ_RELEASE_ASSERT(mainThreadId == mainTestThreadId,
"Test should run on the main thread");
MOZ_RELEASE_ASSERT(mozilla::baseprofiler::profiler_is_main_thread());
std::thread testThread([&]() {
const BaseProfilerThreadId testThreadId =
mozilla::baseprofiler::profiler_current_thread_id();
MOZ_RELEASE_ASSERT(testThreadId.IsSpecified());
MOZ_RELEASE_ASSERT(testThreadId != mainThreadId);
MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_is_main_thread());
});
testThread.join();
}
// No conversions between processes and threads.
static_assert(
!std::is_constructible_v<mozilla::baseprofiler::BaseProfilerThreadId,
mozilla::baseprofiler::BaseProfilerProcessId>);
static_assert(
!std::is_assignable_v<mozilla::baseprofiler::BaseProfilerThreadId,
mozilla::baseprofiler::BaseProfilerProcessId>);
static_assert(
!std::is_constructible_v<mozilla::baseprofiler::BaseProfilerProcessId,
mozilla::baseprofiler::BaseProfilerThreadId>);
static_assert(
!std::is_assignable_v<mozilla::baseprofiler::BaseProfilerProcessId,
mozilla::baseprofiler::BaseProfilerThreadId>);
printf("TestProfilerUtils done\n");
}
void TestBaseAndProfilerDetail() {
printf("TestBaseAndProfilerDetail...\n");
{
using mozilla::profiler::detail::FilterHasPid;
const auto pid123 =
mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(123);
MOZ_RELEASE_ASSERT(FilterHasPid("pid:123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid(" ", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid=123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:123 ", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid: 123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0000000000000000000000123", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:12", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:1234", pid123));
MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0", pid123));
using PidNumber = mozilla::baseprofiler::BaseProfilerProcessId::NumberType;
const PidNumber maxNumber = std::numeric_limits<PidNumber>::max();
const auto maxPid =
mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(maxNumber);
const std::string maxPidString = "pid:" + std::to_string(maxNumber);
MOZ_RELEASE_ASSERT(FilterHasPid(maxPidString.c_str(), maxPid));
const std::string tooBigPidString = maxPidString + "0";
MOZ_RELEASE_ASSERT(!FilterHasPid(tooBigPidString.c_str(), maxPid));
}
{
using mozilla::profiler::detail::FiltersExcludePid;
const auto pid123 =
mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(123);
MOZ_RELEASE_ASSERT(
!FiltersExcludePid(mozilla::Span<const char*>{}, pid123));
{
const char* const filters[] = {"main"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"main", "pid:123"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"main", "pid:456"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:123"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:123", "pid:456"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:456", "pid:123"};
MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:456"};
MOZ_RELEASE_ASSERT(FiltersExcludePid(filters, pid123));
}
{
const char* const filters[] = {"pid:456", "pid:789"};
MOZ_RELEASE_ASSERT(FiltersExcludePid(filters, pid123));
}
}
printf("TestBaseAndProfilerDetail done\n");
}
void TestSharedMutex() {
printf("TestSharedMutex...\n");
mozilla::baseprofiler::detail::BaseProfilerSharedMutex sm;
// First round of minimal tests in this thread.
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
sm.LockExclusive();
MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
sm.UnlockExclusive();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
sm.LockShared();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
sm.UnlockShared();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
{
mozilla::baseprofiler::detail::BaseProfilerAutoLockExclusive exclusiveLock{
sm};
MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
}
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
{
mozilla::baseprofiler::detail::BaseProfilerAutoLockShared sharedLock{sm};
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
}
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
// The following will run actions between two threads, to verify that
// exclusive and shared locks work as expected.
// These actions will happen from top to bottom.
// This will test all possible lock interactions.
enum NextAction { // State of the lock:
t1Starting, // (x=exclusive, s=shared, ?=blocked)
t2Starting, // t1 t2
t1LockExclusive, // x
t2LockExclusiveAndBlock, // x x? - Can't have two exclusives.
t1UnlockExclusive, // x
t2UnblockedAfterT1Unlock, // x
t1LockSharedAndBlock, // s? x - Can't have shared during excl
t2UnlockExclusive, // s
t1UnblockedAfterT2Unlock, // s
t2LockShared, // s s - Can have multiple shared locks
t1UnlockShared, // s
t2StillLockedShared, // s
t1LockExclusiveAndBlock, // x? s - Can't have excl during shared
t2UnlockShared, // x
t1UnblockedAfterT2UnlockShared, // x
t2CheckAfterT1Lock, // x
t1LastUnlockExclusive, // (unlocked)
done
};
// Each thread will repeatedly read this `nextAction`, and run actions that
// target it...
std::atomic<NextAction> nextAction{static_cast<NextAction>(0)};
// ... and advance to the next available action (which should usually be for
// the other thread).
auto AdvanceAction = [&nextAction]() {
MOZ_RELEASE_ASSERT(nextAction <= done);
nextAction = static_cast<NextAction>(static_cast<int>(nextAction) + 1);
};
std::thread t1{[&]() {
for (;;) {
switch (nextAction) {
case t1Starting:
AdvanceAction();
break;
case t1LockExclusive:
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
sm.LockExclusive();
MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
AdvanceAction();
break;
case t1UnlockExclusive:
MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
// Advance first, before unlocking, so that t2 sees the new state.
AdvanceAction();
sm.UnlockExclusive();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
break;
case t1LockSharedAndBlock:
// Advance action before attempting to lock after t2's exclusive lock.
AdvanceAction();
sm.LockShared();
// We will only acquire the lock after t1 unlocks.
MOZ_RELEASE_ASSERT(nextAction == t1UnblockedAfterT2Unlock);
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
AdvanceAction();
break;
case t1UnlockShared:
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
// Advance first, before unlocking, so that t2 sees the new state.
AdvanceAction();
sm.UnlockShared();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
break;
case t1LockExclusiveAndBlock:
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
// Advance action before attempting to lock after t2's shared lock.
AdvanceAction();
sm.LockExclusive();
// We will only acquire the lock after t2 unlocks.
MOZ_RELEASE_ASSERT(nextAction == t1UnblockedAfterT2UnlockShared);
MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
AdvanceAction();
break;
case t1LastUnlockExclusive:
MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
// Advance first, before unlocking, so that t2 sees the new state.
AdvanceAction();
sm.UnlockExclusive();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
break;
case done:
return;
default:
// Ignore other actions intended for t2.
break;
}
}
}};
std::thread t2{[&]() {
for (;;) {
switch (nextAction) {
case t2Starting:
AdvanceAction();
break;
case t2LockExclusiveAndBlock:
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
// Advance action before attempting to lock after t1's exclusive lock.
AdvanceAction();
sm.LockExclusive();
// We will only acquire the lock after t1 unlocks.
MOZ_RELEASE_ASSERT(nextAction == t2UnblockedAfterT1Unlock);
MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
AdvanceAction();
break;
case t2UnlockExclusive:
MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
// Advance first, before unlocking, so that t1 sees the new state.
AdvanceAction();
sm.UnlockExclusive();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
break;
case t2LockShared:
sm.LockShared();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
AdvanceAction();
break;
case t2StillLockedShared:
AdvanceAction();
break;
case t2UnlockShared:
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
// Advance first, before unlocking, so that t1 sees the new state.
AdvanceAction();
sm.UnlockShared();
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
break;
case t2CheckAfterT1Lock:
MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
AdvanceAction();
break;
case done:
return;
default:
// Ignore other actions intended for t1.
break;
}
}
}};
t1.join();
t2.join();
printf("TestSharedMutex done\n");
}
void TestProportionValue() {
printf("TestProportionValue...\n");
using mozilla::ProportionValue;
#define STATIC_ASSERT_EQ(a, b) \
static_assert((a) == (b)); \
MOZ_RELEASE_ASSERT((a) == (b));
#define STATIC_ASSERT(e) STATIC_ASSERT_EQ(e, true)
// Conversion from&to double.
STATIC_ASSERT_EQ(ProportionValue().ToDouble(), 0.0);
STATIC_ASSERT_EQ(ProportionValue(0.0).ToDouble(), 0.0);
STATIC_ASSERT_EQ(ProportionValue(0.5).ToDouble(), 0.5);
STATIC_ASSERT_EQ(ProportionValue(1.0).ToDouble(), 1.0);
// Clamping.
STATIC_ASSERT_EQ(
ProportionValue(std::numeric_limits<double>::min()).ToDouble(), 0.0);
STATIC_ASSERT_EQ(
ProportionValue(std::numeric_limits<long double>::min()).ToDouble(), 0.0);
STATIC_ASSERT_EQ(ProportionValue(-1.0).ToDouble(), 0.0);
STATIC_ASSERT_EQ(ProportionValue(-0.01).ToDouble(), 0.0);
STATIC_ASSERT_EQ(ProportionValue(-0.0).ToDouble(), 0.0);
STATIC_ASSERT_EQ(ProportionValue(1.01).ToDouble(), 1.0);
STATIC_ASSERT_EQ(
ProportionValue(std::numeric_limits<double>::max()).ToDouble(), 1.0);
// User-defined literal.
{
using namespace mozilla::literals::ProportionValue_literals;
STATIC_ASSERT_EQ(0_pc, ProportionValue(0.0));
STATIC_ASSERT_EQ(0._pc, ProportionValue(0.0));
STATIC_ASSERT_EQ(50_pc, ProportionValue(0.5));
STATIC_ASSERT_EQ(50._pc, ProportionValue(0.5));
STATIC_ASSERT_EQ(100_pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(100._pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(101_pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(100.01_pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(1000_pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(1000._pc, ProportionValue(1.0));
}
{
// ProportionValue_literals is an inline namespace of mozilla::literals, so
// it's optional.
using namespace mozilla::literals;
STATIC_ASSERT_EQ(0_pc, ProportionValue(0.0));
STATIC_ASSERT_EQ(0._pc, ProportionValue(0.0));
STATIC_ASSERT_EQ(50_pc, ProportionValue(0.5));
STATIC_ASSERT_EQ(50._pc, ProportionValue(0.5));
STATIC_ASSERT_EQ(100_pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(100._pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(101_pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(100.01_pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(1000_pc, ProportionValue(1.0));
STATIC_ASSERT_EQ(1000._pc, ProportionValue(1.0));
}
// Invalid construction, conversion to double NaN.
MOZ_RELEASE_ASSERT(std::isnan(ProportionValue::MakeInvalid().ToDouble()));
using namespace mozilla::literals::ProportionValue_literals;
// Conversion to&from underlying integral number.
STATIC_ASSERT_EQ(
ProportionValue::FromUnderlyingType((0_pc).ToUnderlyingType()).ToDouble(),
0.0);
STATIC_ASSERT_EQ(
ProportionValue::FromUnderlyingType((50_pc).ToUnderlyingType())
.ToDouble(),
0.5);
STATIC_ASSERT_EQ(
ProportionValue::FromUnderlyingType((100_pc).ToUnderlyingType())
.ToDouble(),
1.0);
STATIC_ASSERT(ProportionValue::FromUnderlyingType(
ProportionValue::MakeInvalid().ToUnderlyingType())
.IsInvalid());
// IsExactlyZero.
STATIC_ASSERT(ProportionValue().IsExactlyZero());
STATIC_ASSERT((0_pc).IsExactlyZero());
STATIC_ASSERT(!(50_pc).IsExactlyZero());
STATIC_ASSERT(!(100_pc).IsExactlyZero());
STATIC_ASSERT(!ProportionValue::MakeInvalid().IsExactlyZero());
// IsExactlyOne.
STATIC_ASSERT(!ProportionValue().IsExactlyOne());
STATIC_ASSERT(!(0_pc).IsExactlyOne());
STATIC_ASSERT(!(50_pc).IsExactlyOne());
STATIC_ASSERT((100_pc).IsExactlyOne());
STATIC_ASSERT(!ProportionValue::MakeInvalid().IsExactlyOne());
// IsValid.
STATIC_ASSERT(ProportionValue().IsValid());
STATIC_ASSERT((0_pc).IsValid());
STATIC_ASSERT((50_pc).IsValid());
STATIC_ASSERT((100_pc).IsValid());
STATIC_ASSERT(!ProportionValue::MakeInvalid().IsValid());
// IsInvalid.
STATIC_ASSERT(!ProportionValue().IsInvalid());
STATIC_ASSERT(!(0_pc).IsInvalid());
STATIC_ASSERT(!(50_pc).IsInvalid());
STATIC_ASSERT(!(100_pc).IsInvalid());
STATIC_ASSERT(ProportionValue::MakeInvalid().IsInvalid());
// Addition.
STATIC_ASSERT_EQ((0_pc + 0_pc).ToDouble(), 0.0);
STATIC_ASSERT_EQ((0_pc + 100_pc).ToDouble(), 1.0);
STATIC_ASSERT_EQ((100_pc + 0_pc).ToDouble(), 1.0);