tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestBaseProfiler.cpp (193259B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/Attributes.h"
      8 #include "mozilla/BaseAndGeckoProfilerDetail.h"
      9 #include "mozilla/BaseProfileJSONWriter.h"
     10 #include "mozilla/BaseProfiler.h"
     11 #include "mozilla/BaseProfilerDetail.h"
     12 #include "mozilla/FailureLatch.h"
     13 #include "mozilla/NotNull.h"
     14 #include "mozilla/ProgressLogger.h"
     15 #include "mozilla/ProportionValue.h"
     16 
     17 #ifdef MOZ_GECKO_PROFILER
     18 #  include "mozilla/BaseProfilerMarkerTypes.h"
     19 #  include "mozilla/leb128iterator.h"
     20 #  include "mozilla/ModuloBuffer.h"
     21 #  include "mozilla/mozalloc.h"
     22 #  include "mozilla/PowerOfTwo.h"
     23 #  include "mozilla/ProfileBufferChunk.h"
     24 #  include "mozilla/ProfileBufferChunkManagerSingle.h"
     25 #  include "mozilla/ProfileBufferChunkManagerWithLocalLimit.h"
     26 #  include "mozilla/ProfileBufferControlledChunkManager.h"
     27 #  include "mozilla/ProfileChunkedBuffer.h"
     28 #  include "mozilla/Vector.h"
     29 #endif  // MOZ_GECKO_PROFILER
     30 
     31 #if defined(_MSC_VER) || defined(__MINGW32__)
     32 #  include <windows.h>
     33 #  include <mmsystem.h>
     34 #  include <process.h>
     35 #else
     36 #  include <errno.h>
     37 #  include <time.h>
     38 #endif
     39 
     40 #include <algorithm>
     41 #include <atomic>
     42 #include <iostream>
     43 #include <random>
     44 #include <thread>
     45 #include <type_traits>
     46 #include <utility>
     47 
     48 void TestFailureLatch() {
     49  printf("TestFailureLatch...\n");
     50 
     51  // Test infallible latch.
     52  {
     53    mozilla::FailureLatchInfallibleSource& infallibleLatch =
     54        mozilla::FailureLatchInfallibleSource::Singleton();
     55 
     56    MOZ_RELEASE_ASSERT(!infallibleLatch.Fallible());
     57    MOZ_RELEASE_ASSERT(!infallibleLatch.Failed());
     58    MOZ_RELEASE_ASSERT(!infallibleLatch.GetFailure());
     59    MOZ_RELEASE_ASSERT(&infallibleLatch.SourceFailureLatch() ==
     60                       &mozilla::FailureLatchInfallibleSource::Singleton());
     61    MOZ_RELEASE_ASSERT(&std::as_const(infallibleLatch).SourceFailureLatch() ==
     62                       &mozilla::FailureLatchInfallibleSource::Singleton());
     63  }
     64 
     65  // Test failure latch basic functions.
     66  {
     67    mozilla::FailureLatchSource failureLatch;
     68 
     69    MOZ_RELEASE_ASSERT(failureLatch.Fallible());
     70    MOZ_RELEASE_ASSERT(!failureLatch.Failed());
     71    MOZ_RELEASE_ASSERT(!failureLatch.GetFailure());
     72    MOZ_RELEASE_ASSERT(&failureLatch.SourceFailureLatch() == &failureLatch);
     73    MOZ_RELEASE_ASSERT(&std::as_const(failureLatch).SourceFailureLatch() ==
     74                       &failureLatch);
     75 
     76    failureLatch.SetFailure("error");
     77 
     78    MOZ_RELEASE_ASSERT(failureLatch.Fallible());
     79    MOZ_RELEASE_ASSERT(failureLatch.Failed());
     80    MOZ_RELEASE_ASSERT(failureLatch.GetFailure());
     81    MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
     82 
     83    failureLatch.SetFailure("later error");
     84 
     85    MOZ_RELEASE_ASSERT(failureLatch.Fallible());
     86    MOZ_RELEASE_ASSERT(failureLatch.Failed());
     87    MOZ_RELEASE_ASSERT(failureLatch.GetFailure());
     88    MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
     89  }
     90 
     91  // Test SetFailureFrom.
     92  {
     93    mozilla::FailureLatchSource failureLatch;
     94 
     95    MOZ_RELEASE_ASSERT(!failureLatch.Failed());
     96    failureLatch.SetFailureFrom(failureLatch);
     97    MOZ_RELEASE_ASSERT(!failureLatch.Failed());
     98    MOZ_RELEASE_ASSERT(!failureLatch.GetFailure());
     99 
    100    // SetFailureFrom with no error.
    101    {
    102      mozilla::FailureLatchSource failureLatchInnerOk;
    103      MOZ_RELEASE_ASSERT(!failureLatchInnerOk.Failed());
    104      MOZ_RELEASE_ASSERT(!failureLatchInnerOk.GetFailure());
    105 
    106      MOZ_RELEASE_ASSERT(!failureLatch.Failed());
    107      failureLatch.SetFailureFrom(failureLatchInnerOk);
    108      MOZ_RELEASE_ASSERT(!failureLatch.Failed());
    109 
    110      MOZ_RELEASE_ASSERT(!failureLatchInnerOk.Failed());
    111      MOZ_RELEASE_ASSERT(!failureLatchInnerOk.GetFailure());
    112    }
    113    MOZ_RELEASE_ASSERT(!failureLatch.Failed());
    114    MOZ_RELEASE_ASSERT(!failureLatch.GetFailure());
    115 
    116    // SetFailureFrom with error.
    117    {
    118      mozilla::FailureLatchSource failureLatchInnerError;
    119      MOZ_RELEASE_ASSERT(!failureLatchInnerError.Failed());
    120      MOZ_RELEASE_ASSERT(!failureLatchInnerError.GetFailure());
    121 
    122      failureLatchInnerError.SetFailure("inner error");
    123      MOZ_RELEASE_ASSERT(failureLatchInnerError.Failed());
    124      MOZ_RELEASE_ASSERT(
    125          strcmp(failureLatchInnerError.GetFailure(), "inner error") == 0);
    126 
    127      MOZ_RELEASE_ASSERT(!failureLatch.Failed());
    128      failureLatch.SetFailureFrom(failureLatchInnerError);
    129      MOZ_RELEASE_ASSERT(failureLatch.Failed());
    130 
    131      MOZ_RELEASE_ASSERT(failureLatchInnerError.Failed());
    132      MOZ_RELEASE_ASSERT(
    133          strcmp(failureLatchInnerError.GetFailure(), "inner error") == 0);
    134    }
    135    MOZ_RELEASE_ASSERT(failureLatch.Failed());
    136    MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "inner error") == 0);
    137 
    138    failureLatch.SetFailureFrom(failureLatch);
    139    MOZ_RELEASE_ASSERT(failureLatch.Failed());
    140    MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "inner error") == 0);
    141 
    142    // SetFailureFrom with error again, ignored.
    143    {
    144      mozilla::FailureLatchSource failureLatchInnerError;
    145      failureLatchInnerError.SetFailure("later inner error");
    146      MOZ_RELEASE_ASSERT(failureLatchInnerError.Failed());
    147      MOZ_RELEASE_ASSERT(strcmp(failureLatchInnerError.GetFailure(),
    148                                "later inner error") == 0);
    149 
    150      MOZ_RELEASE_ASSERT(failureLatch.Failed());
    151      failureLatch.SetFailureFrom(failureLatchInnerError);
    152      MOZ_RELEASE_ASSERT(failureLatch.Failed());
    153 
    154      MOZ_RELEASE_ASSERT(failureLatchInnerError.Failed());
    155      MOZ_RELEASE_ASSERT(strcmp(failureLatchInnerError.GetFailure(),
    156                                "later inner error") == 0);
    157    }
    158    MOZ_RELEASE_ASSERT(failureLatch.Failed());
    159    MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "inner error") == 0);
    160  }
    161 
    162  // Test FAILURELATCH_IMPL_PROXY
    163  {
    164    class Proxy final : public mozilla::FailureLatch {
    165     public:
    166      explicit Proxy(mozilla::FailureLatch& aFailureLatch)
    167          : mFailureLatch(WrapNotNull(&aFailureLatch)) {}
    168 
    169      void Set(mozilla::FailureLatch& aFailureLatch) {
    170        mFailureLatch = WrapNotNull(&aFailureLatch);
    171      }
    172 
    173      FAILURELATCH_IMPL_PROXY(*mFailureLatch)
    174 
    175     private:
    176      mozilla::NotNull<mozilla::FailureLatch*> mFailureLatch;
    177    };
    178 
    179    Proxy proxy{mozilla::FailureLatchInfallibleSource::Singleton()};
    180 
    181    MOZ_RELEASE_ASSERT(!proxy.Fallible());
    182    MOZ_RELEASE_ASSERT(!proxy.Failed());
    183    MOZ_RELEASE_ASSERT(!proxy.GetFailure());
    184    MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() ==
    185                       &mozilla::FailureLatchInfallibleSource::Singleton());
    186    MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
    187                       &mozilla::FailureLatchInfallibleSource::Singleton());
    188 
    189    // Error from proxy.
    190    {
    191      mozilla::FailureLatchSource failureLatch;
    192      proxy.Set(failureLatch);
    193      MOZ_RELEASE_ASSERT(proxy.Fallible());
    194      MOZ_RELEASE_ASSERT(!proxy.Failed());
    195      MOZ_RELEASE_ASSERT(!proxy.GetFailure());
    196      MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() == &failureLatch);
    197      MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
    198                         &failureLatch);
    199 
    200      proxy.SetFailure("error");
    201      MOZ_RELEASE_ASSERT(proxy.Failed());
    202      MOZ_RELEASE_ASSERT(strcmp(proxy.GetFailure(), "error") == 0);
    203      MOZ_RELEASE_ASSERT(failureLatch.Failed());
    204      MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
    205 
    206      // Don't forget to stop pointing at soon-to-be-destroyed object.
    207      proxy.Set(mozilla::FailureLatchInfallibleSource::Singleton());
    208    }
    209 
    210    // Error from proxy's origin.
    211    {
    212      mozilla::FailureLatchSource failureLatch;
    213      proxy.Set(failureLatch);
    214      MOZ_RELEASE_ASSERT(proxy.Fallible());
    215      MOZ_RELEASE_ASSERT(!proxy.Failed());
    216      MOZ_RELEASE_ASSERT(!proxy.GetFailure());
    217      MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() == &failureLatch);
    218      MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
    219                         &failureLatch);
    220 
    221      failureLatch.SetFailure("error");
    222      MOZ_RELEASE_ASSERT(proxy.Failed());
    223      MOZ_RELEASE_ASSERT(strcmp(proxy.GetFailure(), "error") == 0);
    224      MOZ_RELEASE_ASSERT(failureLatch.Failed());
    225      MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
    226 
    227      // Don't forget to stop pointing at soon-to-be-destroyed object.
    228      proxy.Set(mozilla::FailureLatchInfallibleSource::Singleton());
    229    }
    230 
    231    MOZ_RELEASE_ASSERT(!proxy.Fallible());
    232    MOZ_RELEASE_ASSERT(!proxy.Failed());
    233    MOZ_RELEASE_ASSERT(!proxy.GetFailure());
    234    MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() ==
    235                       &mozilla::FailureLatchInfallibleSource::Singleton());
    236    MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
    237                       &mozilla::FailureLatchInfallibleSource::Singleton());
    238  }
    239 
    240  // Test FAILURELATCH_IMPL_PROXY_OR_INFALLIBLE
    241  {
    242    class ProxyOrNull final : public mozilla::FailureLatch {
    243     public:
    244      ProxyOrNull() = default;
    245 
    246      void Set(mozilla::FailureLatch* aFailureLatchOrNull) {
    247        mFailureLatchOrNull = aFailureLatchOrNull;
    248      }
    249 
    250      FAILURELATCH_IMPL_PROXY_OR_INFALLIBLE(mFailureLatchOrNull, ProxyOrNull)
    251 
    252     private:
    253      mozilla::FailureLatch* mFailureLatchOrNull = nullptr;
    254    };
    255 
    256    ProxyOrNull proxy;
    257 
    258    MOZ_RELEASE_ASSERT(!proxy.Fallible());
    259    MOZ_RELEASE_ASSERT(!proxy.Failed());
    260    MOZ_RELEASE_ASSERT(!proxy.GetFailure());
    261    MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() ==
    262                       &mozilla::FailureLatchInfallibleSource::Singleton());
    263    MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
    264                       &mozilla::FailureLatchInfallibleSource::Singleton());
    265 
    266    // Error from proxy.
    267    {
    268      mozilla::FailureLatchSource failureLatch;
    269      proxy.Set(&failureLatch);
    270      MOZ_RELEASE_ASSERT(proxy.Fallible());
    271      MOZ_RELEASE_ASSERT(!proxy.Failed());
    272      MOZ_RELEASE_ASSERT(!proxy.GetFailure());
    273      MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() == &failureLatch);
    274      MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
    275                         &failureLatch);
    276 
    277      proxy.SetFailure("error");
    278      MOZ_RELEASE_ASSERT(proxy.Failed());
    279      MOZ_RELEASE_ASSERT(strcmp(proxy.GetFailure(), "error") == 0);
    280      MOZ_RELEASE_ASSERT(failureLatch.Failed());
    281      MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
    282 
    283      // Don't forget to stop pointing at soon-to-be-destroyed object.
    284      proxy.Set(nullptr);
    285    }
    286 
    287    // Error from proxy's origin.
    288    {
    289      mozilla::FailureLatchSource failureLatch;
    290      proxy.Set(&failureLatch);
    291      MOZ_RELEASE_ASSERT(proxy.Fallible());
    292      MOZ_RELEASE_ASSERT(!proxy.Failed());
    293      MOZ_RELEASE_ASSERT(!proxy.GetFailure());
    294      MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() == &failureLatch);
    295      MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
    296                         &failureLatch);
    297 
    298      failureLatch.SetFailure("error");
    299      MOZ_RELEASE_ASSERT(proxy.Failed());
    300      MOZ_RELEASE_ASSERT(strcmp(proxy.GetFailure(), "error") == 0);
    301      MOZ_RELEASE_ASSERT(failureLatch.Failed());
    302      MOZ_RELEASE_ASSERT(strcmp(failureLatch.GetFailure(), "error") == 0);
    303 
    304      // Don't forget to stop pointing at soon-to-be-destroyed object.
    305      proxy.Set(nullptr);
    306    }
    307 
    308    MOZ_RELEASE_ASSERT(!proxy.Fallible());
    309    MOZ_RELEASE_ASSERT(!proxy.Failed());
    310    MOZ_RELEASE_ASSERT(!proxy.GetFailure());
    311    MOZ_RELEASE_ASSERT(&proxy.SourceFailureLatch() ==
    312                       &mozilla::FailureLatchInfallibleSource::Singleton());
    313    MOZ_RELEASE_ASSERT(&std::as_const(proxy).SourceFailureLatch() ==
    314                       &mozilla::FailureLatchInfallibleSource::Singleton());
    315  }
    316 
    317  printf("TestFailureLatch done\n");
    318 }
    319 
    320 void TestProfilerUtils() {
    321  printf("TestProfilerUtils...\n");
    322 
    323  {
    324    using mozilla::baseprofiler::BaseProfilerProcessId;
    325    using Number = BaseProfilerProcessId::NumberType;
    326    static constexpr Number scMaxNumber = std::numeric_limits<Number>::max();
    327 
    328    static_assert(
    329        BaseProfilerProcessId{}.ToNumber() == 0,
    330        "These tests assume that the unspecified process id number is 0; "
    331        "if this fails, please update these tests accordingly");
    332 
    333    static_assert(!BaseProfilerProcessId{}.IsSpecified());
    334    static_assert(!BaseProfilerProcessId::FromNumber(0).IsSpecified());
    335    static_assert(BaseProfilerProcessId::FromNumber(1).IsSpecified());
    336    static_assert(BaseProfilerProcessId::FromNumber(123).IsSpecified());
    337    static_assert(BaseProfilerProcessId::FromNumber(scMaxNumber).IsSpecified());
    338 
    339    static_assert(BaseProfilerProcessId::FromNumber(Number(1)).ToNumber() ==
    340                  Number(1));
    341    static_assert(BaseProfilerProcessId::FromNumber(Number(123)).ToNumber() ==
    342                  Number(123));
    343    static_assert(BaseProfilerProcessId::FromNumber(scMaxNumber).ToNumber() ==
    344                  scMaxNumber);
    345 
    346    static_assert(BaseProfilerProcessId{} == BaseProfilerProcessId{});
    347    static_assert(BaseProfilerProcessId::FromNumber(Number(123)) ==
    348                  BaseProfilerProcessId::FromNumber(Number(123)));
    349    static_assert(BaseProfilerProcessId{} !=
    350                  BaseProfilerProcessId::FromNumber(Number(123)));
    351    static_assert(BaseProfilerProcessId::FromNumber(Number(123)) !=
    352                  BaseProfilerProcessId{});
    353    static_assert(BaseProfilerProcessId::FromNumber(Number(123)) !=
    354                  BaseProfilerProcessId::FromNumber(scMaxNumber));
    355    static_assert(BaseProfilerProcessId::FromNumber(scMaxNumber) !=
    356                  BaseProfilerProcessId::FromNumber(Number(123)));
    357 
    358    // Verify trivial-copyability by memcpy'ing to&from same-size storage.
    359    static_assert(std::is_trivially_copyable_v<BaseProfilerProcessId>);
    360    BaseProfilerProcessId pid;
    361    MOZ_RELEASE_ASSERT(!pid.IsSpecified());
    362    Number pidStorage;
    363    static_assert(sizeof(pidStorage) == sizeof(pid));
    364    // Copy from BaseProfilerProcessId to storage. Note: We cannot assume that
    365    // this is equal to what ToNumber() gives us. All we can do is verify that
    366    // copying from storage back to BaseProfilerProcessId works as expected.
    367    std::memcpy(&pidStorage, &pid, sizeof(pidStorage));
    368    BaseProfilerProcessId pid2 = BaseProfilerProcessId::FromNumber(2);
    369    MOZ_RELEASE_ASSERT(pid2.IsSpecified());
    370    std::memcpy(&pid2, &pidStorage, sizeof(pid));
    371    MOZ_RELEASE_ASSERT(!pid2.IsSpecified());
    372 
    373    pid = BaseProfilerProcessId::FromNumber(123);
    374    std::memcpy(&pidStorage, &pid, sizeof(pidStorage));
    375    pid2 = BaseProfilerProcessId{};
    376    MOZ_RELEASE_ASSERT(!pid2.IsSpecified());
    377    std::memcpy(&pid2, &pidStorage, sizeof(pid));
    378    MOZ_RELEASE_ASSERT(pid2.IsSpecified());
    379    MOZ_RELEASE_ASSERT(pid2.ToNumber() == 123);
    380 
    381    // No conversions to/from numbers.
    382    static_assert(!std::is_constructible_v<BaseProfilerProcessId, Number>);
    383    static_assert(!std::is_assignable_v<BaseProfilerProcessId, Number>);
    384    static_assert(!std::is_constructible_v<Number, BaseProfilerProcessId>);
    385    static_assert(!std::is_assignable_v<Number, BaseProfilerProcessId>);
    386 
    387    static_assert(
    388        std::is_same_v<
    389            decltype(mozilla::baseprofiler::profiler_current_process_id()),
    390            BaseProfilerProcessId>);
    391    MOZ_RELEASE_ASSERT(
    392        mozilla::baseprofiler::profiler_current_process_id().IsSpecified());
    393  }
    394 
    395  {
    396    mozilla::baseprofiler::profiler_init_main_thread_id();
    397 
    398    using mozilla::baseprofiler::BaseProfilerThreadId;
    399    using Number = BaseProfilerThreadId::NumberType;
    400    static constexpr Number scMaxNumber = std::numeric_limits<Number>::max();
    401 
    402    static_assert(
    403        BaseProfilerThreadId{}.ToNumber() == 0,
    404        "These tests assume that the unspecified thread id number is 0; "
    405        "if this fails, please update these tests accordingly");
    406 
    407    static_assert(!BaseProfilerThreadId{}.IsSpecified());
    408    static_assert(!BaseProfilerThreadId::FromNumber(0).IsSpecified());
    409    static_assert(BaseProfilerThreadId::FromNumber(1).IsSpecified());
    410    static_assert(BaseProfilerThreadId::FromNumber(123).IsSpecified());
    411    static_assert(BaseProfilerThreadId::FromNumber(scMaxNumber).IsSpecified());
    412 
    413    static_assert(BaseProfilerThreadId::FromNumber(Number(1)).ToNumber() ==
    414                  Number(1));
    415    static_assert(BaseProfilerThreadId::FromNumber(Number(123)).ToNumber() ==
    416                  Number(123));
    417    static_assert(BaseProfilerThreadId::FromNumber(scMaxNumber).ToNumber() ==
    418                  scMaxNumber);
    419 
    420    static_assert(BaseProfilerThreadId{} == BaseProfilerThreadId{});
    421    static_assert(BaseProfilerThreadId::FromNumber(Number(123)) ==
    422                  BaseProfilerThreadId::FromNumber(Number(123)));
    423    static_assert(BaseProfilerThreadId{} !=
    424                  BaseProfilerThreadId::FromNumber(Number(123)));
    425    static_assert(BaseProfilerThreadId::FromNumber(Number(123)) !=
    426                  BaseProfilerThreadId{});
    427    static_assert(BaseProfilerThreadId::FromNumber(Number(123)) !=
    428                  BaseProfilerThreadId::FromNumber(scMaxNumber));
    429    static_assert(BaseProfilerThreadId::FromNumber(scMaxNumber) !=
    430                  BaseProfilerThreadId::FromNumber(Number(123)));
    431 
    432    // Verify trivial-copyability by memcpy'ing to&from same-size storage.
    433    static_assert(std::is_trivially_copyable_v<BaseProfilerThreadId>);
    434    BaseProfilerThreadId tid;
    435    MOZ_RELEASE_ASSERT(!tid.IsSpecified());
    436    Number tidStorage;
    437    static_assert(sizeof(tidStorage) == sizeof(tid));
    438    // Copy from BaseProfilerThreadId to storage. Note: We cannot assume that
    439    // this is equal to what ToNumber() gives us. All we can do is verify that
    440    // copying from storage back to BaseProfilerThreadId works as expected.
    441    std::memcpy(&tidStorage, &tid, sizeof(tidStorage));
    442    BaseProfilerThreadId tid2 = BaseProfilerThreadId::FromNumber(2);
    443    MOZ_RELEASE_ASSERT(tid2.IsSpecified());
    444    std::memcpy(&tid2, &tidStorage, sizeof(tid));
    445    MOZ_RELEASE_ASSERT(!tid2.IsSpecified());
    446 
    447    tid = BaseProfilerThreadId::FromNumber(Number(123));
    448    std::memcpy(&tidStorage, &tid, sizeof(tidStorage));
    449    tid2 = BaseProfilerThreadId{};
    450    MOZ_RELEASE_ASSERT(!tid2.IsSpecified());
    451    std::memcpy(&tid2, &tidStorage, sizeof(tid));
    452    MOZ_RELEASE_ASSERT(tid2.IsSpecified());
    453    MOZ_RELEASE_ASSERT(tid2.ToNumber() == Number(123));
    454 
    455    // No conversions to/from numbers.
    456    static_assert(!std::is_constructible_v<BaseProfilerThreadId, Number>);
    457    static_assert(!std::is_assignable_v<BaseProfilerThreadId, Number>);
    458    static_assert(!std::is_constructible_v<Number, BaseProfilerThreadId>);
    459    static_assert(!std::is_assignable_v<Number, BaseProfilerThreadId>);
    460 
    461    static_assert(std::is_same_v<
    462                  decltype(mozilla::baseprofiler::profiler_current_thread_id()),
    463                  BaseProfilerThreadId>);
    464    BaseProfilerThreadId mainTestThreadId =
    465        mozilla::baseprofiler::profiler_current_thread_id();
    466    MOZ_RELEASE_ASSERT(mainTestThreadId.IsSpecified());
    467 
    468    BaseProfilerThreadId mainThreadId =
    469        mozilla::baseprofiler::profiler_main_thread_id();
    470    MOZ_RELEASE_ASSERT(mainThreadId.IsSpecified());
    471 
    472    MOZ_RELEASE_ASSERT(mainThreadId == mainTestThreadId,
    473                       "Test should run on the main thread");
    474    MOZ_RELEASE_ASSERT(mozilla::baseprofiler::profiler_is_main_thread());
    475 
    476    std::thread testThread([&]() {
    477      const BaseProfilerThreadId testThreadId =
    478          mozilla::baseprofiler::profiler_current_thread_id();
    479      MOZ_RELEASE_ASSERT(testThreadId.IsSpecified());
    480      MOZ_RELEASE_ASSERT(testThreadId != mainThreadId);
    481      MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_is_main_thread());
    482    });
    483    testThread.join();
    484  }
    485 
    486  // No conversions between processes and threads.
    487  static_assert(
    488      !std::is_constructible_v<mozilla::baseprofiler::BaseProfilerThreadId,
    489                               mozilla::baseprofiler::BaseProfilerProcessId>);
    490  static_assert(
    491      !std::is_assignable_v<mozilla::baseprofiler::BaseProfilerThreadId,
    492                            mozilla::baseprofiler::BaseProfilerProcessId>);
    493  static_assert(
    494      !std::is_constructible_v<mozilla::baseprofiler::BaseProfilerProcessId,
    495                               mozilla::baseprofiler::BaseProfilerThreadId>);
    496  static_assert(
    497      !std::is_assignable_v<mozilla::baseprofiler::BaseProfilerProcessId,
    498                            mozilla::baseprofiler::BaseProfilerThreadId>);
    499 
    500  printf("TestProfilerUtils done\n");
    501 }
    502 
    503 void TestBaseAndProfilerDetail() {
    504  printf("TestBaseAndProfilerDetail...\n");
    505 
    506  {
    507    using mozilla::profiler::detail::FilterHasPid;
    508 
    509    const auto pid123 =
    510        mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(123);
    511    MOZ_RELEASE_ASSERT(FilterHasPid("pid:123", pid123));
    512    MOZ_RELEASE_ASSERT(!FilterHasPid("", pid123));
    513    MOZ_RELEASE_ASSERT(!FilterHasPid(" ", pid123));
    514    MOZ_RELEASE_ASSERT(!FilterHasPid("123", pid123));
    515    MOZ_RELEASE_ASSERT(!FilterHasPid("pid", pid123));
    516    MOZ_RELEASE_ASSERT(!FilterHasPid("pid:", pid123));
    517    MOZ_RELEASE_ASSERT(!FilterHasPid("pid=123", pid123));
    518    MOZ_RELEASE_ASSERT(!FilterHasPid("pid:123 ", pid123));
    519    MOZ_RELEASE_ASSERT(!FilterHasPid("pid: 123", pid123));
    520    MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0123", pid123));
    521    MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0000000000000000000000123", pid123));
    522    MOZ_RELEASE_ASSERT(!FilterHasPid("pid:12", pid123));
    523    MOZ_RELEASE_ASSERT(!FilterHasPid("pid:1234", pid123));
    524    MOZ_RELEASE_ASSERT(!FilterHasPid("pid:0", pid123));
    525 
    526    using PidNumber = mozilla::baseprofiler::BaseProfilerProcessId::NumberType;
    527    const PidNumber maxNumber = std::numeric_limits<PidNumber>::max();
    528    const auto maxPid =
    529        mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(maxNumber);
    530    const std::string maxPidString = "pid:" + std::to_string(maxNumber);
    531    MOZ_RELEASE_ASSERT(FilterHasPid(maxPidString.c_str(), maxPid));
    532 
    533    const std::string tooBigPidString = maxPidString + "0";
    534    MOZ_RELEASE_ASSERT(!FilterHasPid(tooBigPidString.c_str(), maxPid));
    535  }
    536 
    537  {
    538    using mozilla::profiler::detail::FiltersExcludePid;
    539    const auto pid123 =
    540        mozilla::baseprofiler::BaseProfilerProcessId::FromNumber(123);
    541 
    542    MOZ_RELEASE_ASSERT(
    543        !FiltersExcludePid(mozilla::Span<const char*>{}, pid123));
    544 
    545    {
    546      const char* const filters[] = {"main"};
    547      MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
    548    }
    549 
    550    {
    551      const char* const filters[] = {"main", "pid:123"};
    552      MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
    553    }
    554 
    555    {
    556      const char* const filters[] = {"main", "pid:456"};
    557      MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
    558    }
    559 
    560    {
    561      const char* const filters[] = {"pid:123"};
    562      MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
    563    }
    564 
    565    {
    566      const char* const filters[] = {"pid:123", "pid:456"};
    567      MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
    568    }
    569 
    570    {
    571      const char* const filters[] = {"pid:456", "pid:123"};
    572      MOZ_RELEASE_ASSERT(!FiltersExcludePid(filters, pid123));
    573    }
    574 
    575    {
    576      const char* const filters[] = {"pid:456"};
    577      MOZ_RELEASE_ASSERT(FiltersExcludePid(filters, pid123));
    578    }
    579 
    580    {
    581      const char* const filters[] = {"pid:456", "pid:789"};
    582      MOZ_RELEASE_ASSERT(FiltersExcludePid(filters, pid123));
    583    }
    584  }
    585 
    586  printf("TestBaseAndProfilerDetail done\n");
    587 }
    588 
    589 void TestSharedMutex() {
    590  printf("TestSharedMutex...\n");
    591 
    592  mozilla::baseprofiler::detail::BaseProfilerSharedMutex sm;
    593 
    594  // First round of minimal tests in this thread.
    595 
    596  MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    597 
    598  sm.LockExclusive();
    599  MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
    600  sm.UnlockExclusive();
    601  MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    602 
    603  sm.LockShared();
    604  MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    605  sm.UnlockShared();
    606  MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    607 
    608  {
    609    mozilla::baseprofiler::detail::BaseProfilerAutoLockExclusive exclusiveLock{
    610        sm};
    611    MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
    612  }
    613  MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    614 
    615  {
    616    mozilla::baseprofiler::detail::BaseProfilerAutoLockShared sharedLock{sm};
    617    MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    618  }
    619  MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    620 
    621  // The following will run actions between two threads, to verify that
    622  // exclusive and shared locks work as expected.
    623 
    624  // These actions will happen from top to bottom.
    625  // This will test all possible lock interactions.
    626  enum NextAction {                  // State of the lock:
    627    t1Starting,                      // (x=exclusive, s=shared, ?=blocked)
    628    t2Starting,                      // t1 t2
    629    t1LockExclusive,                 // x
    630    t2LockExclusiveAndBlock,         // x  x? - Can't have two exclusives.
    631    t1UnlockExclusive,               //    x
    632    t2UnblockedAfterT1Unlock,        //    x
    633    t1LockSharedAndBlock,            // s? x - Can't have shared during excl
    634    t2UnlockExclusive,               // s
    635    t1UnblockedAfterT2Unlock,        // s
    636    t2LockShared,                    // s  s - Can have multiple shared locks
    637    t1UnlockShared,                  //    s
    638    t2StillLockedShared,             //    s
    639    t1LockExclusiveAndBlock,         // x? s - Can't have excl during shared
    640    t2UnlockShared,                  // x
    641    t1UnblockedAfterT2UnlockShared,  // x
    642    t2CheckAfterT1Lock,              // x
    643    t1LastUnlockExclusive,           // (unlocked)
    644    done
    645  };
    646 
    647  // Each thread will repeatedly read this `nextAction`, and run actions that
    648  // target it...
    649  std::atomic<NextAction> nextAction{static_cast<NextAction>(0)};
    650  // ... and advance to the next available action (which should usually be for
    651  // the other thread).
    652  auto AdvanceAction = [&nextAction]() {
    653    MOZ_RELEASE_ASSERT(nextAction <= done);
    654    nextAction = static_cast<NextAction>(static_cast<int>(nextAction) + 1);
    655  };
    656 
    657  std::thread t1{[&]() {
    658    for (;;) {
    659      switch (nextAction) {
    660        case t1Starting:
    661          AdvanceAction();
    662          break;
    663        case t1LockExclusive:
    664          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    665          sm.LockExclusive();
    666          MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
    667          AdvanceAction();
    668          break;
    669        case t1UnlockExclusive:
    670          MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
    671          // Advance first, before unlocking, so that t2 sees the new state.
    672          AdvanceAction();
    673          sm.UnlockExclusive();
    674          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    675          break;
    676        case t1LockSharedAndBlock:
    677          // Advance action before attempting to lock after t2's exclusive lock.
    678          AdvanceAction();
    679          sm.LockShared();
    680          // We will only acquire the lock after t1 unlocks.
    681          MOZ_RELEASE_ASSERT(nextAction == t1UnblockedAfterT2Unlock);
    682          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    683          AdvanceAction();
    684          break;
    685        case t1UnlockShared:
    686          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    687          // Advance first, before unlocking, so that t2 sees the new state.
    688          AdvanceAction();
    689          sm.UnlockShared();
    690          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    691          break;
    692        case t1LockExclusiveAndBlock:
    693          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    694          // Advance action before attempting to lock after t2's shared lock.
    695          AdvanceAction();
    696          sm.LockExclusive();
    697          // We will only acquire the lock after t2 unlocks.
    698          MOZ_RELEASE_ASSERT(nextAction == t1UnblockedAfterT2UnlockShared);
    699          MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
    700          AdvanceAction();
    701          break;
    702        case t1LastUnlockExclusive:
    703          MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
    704          // Advance first, before unlocking, so that t2 sees the new state.
    705          AdvanceAction();
    706          sm.UnlockExclusive();
    707          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    708          break;
    709        case done:
    710          return;
    711        default:
    712          // Ignore other actions intended for t2.
    713          break;
    714      }
    715    }
    716  }};
    717 
    718  std::thread t2{[&]() {
    719    for (;;) {
    720      switch (nextAction) {
    721        case t2Starting:
    722          AdvanceAction();
    723          break;
    724        case t2LockExclusiveAndBlock:
    725          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    726          // Advance action before attempting to lock after t1's exclusive lock.
    727          AdvanceAction();
    728          sm.LockExclusive();
    729          // We will only acquire the lock after t1 unlocks.
    730          MOZ_RELEASE_ASSERT(nextAction == t2UnblockedAfterT1Unlock);
    731          MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
    732          AdvanceAction();
    733          break;
    734        case t2UnlockExclusive:
    735          MOZ_RELEASE_ASSERT(sm.IsLockedExclusiveOnCurrentThread());
    736          // Advance first, before unlocking, so that t1 sees the new state.
    737          AdvanceAction();
    738          sm.UnlockExclusive();
    739          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    740          break;
    741        case t2LockShared:
    742          sm.LockShared();
    743          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    744          AdvanceAction();
    745          break;
    746        case t2StillLockedShared:
    747          AdvanceAction();
    748          break;
    749        case t2UnlockShared:
    750          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    751          // Advance first, before unlocking, so that t1 sees the new state.
    752          AdvanceAction();
    753          sm.UnlockShared();
    754          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    755          break;
    756        case t2CheckAfterT1Lock:
    757          MOZ_RELEASE_ASSERT(!sm.IsLockedExclusiveOnCurrentThread());
    758          AdvanceAction();
    759          break;
    760        case done:
    761          return;
    762        default:
    763          // Ignore other actions intended for t1.
    764          break;
    765      }
    766    }
    767  }};
    768 
    769  t1.join();
    770  t2.join();
    771 
    772  printf("TestSharedMutex done\n");
    773 }
    774 
    775 void TestProportionValue() {
    776  printf("TestProportionValue...\n");
    777 
    778  using mozilla::ProportionValue;
    779 
    780 #define STATIC_ASSERT_EQ(a, b) \
    781  static_assert((a) == (b));   \
    782  MOZ_RELEASE_ASSERT((a) == (b));
    783 
    784 #define STATIC_ASSERT(e) STATIC_ASSERT_EQ(e, true)
    785 
    786  // Conversion from&to double.
    787  STATIC_ASSERT_EQ(ProportionValue().ToDouble(), 0.0);
    788  STATIC_ASSERT_EQ(ProportionValue(0.0).ToDouble(), 0.0);
    789  STATIC_ASSERT_EQ(ProportionValue(0.5).ToDouble(), 0.5);
    790  STATIC_ASSERT_EQ(ProportionValue(1.0).ToDouble(), 1.0);
    791 
    792  // Clamping.
    793  STATIC_ASSERT_EQ(
    794      ProportionValue(std::numeric_limits<double>::min()).ToDouble(), 0.0);
    795  STATIC_ASSERT_EQ(
    796      ProportionValue(std::numeric_limits<long double>::min()).ToDouble(), 0.0);
    797  STATIC_ASSERT_EQ(ProportionValue(-1.0).ToDouble(), 0.0);
    798  STATIC_ASSERT_EQ(ProportionValue(-0.01).ToDouble(), 0.0);
    799  STATIC_ASSERT_EQ(ProportionValue(-0.0).ToDouble(), 0.0);
    800  STATIC_ASSERT_EQ(ProportionValue(1.01).ToDouble(), 1.0);
    801  STATIC_ASSERT_EQ(
    802      ProportionValue(std::numeric_limits<double>::max()).ToDouble(), 1.0);
    803 
    804  // User-defined literal.
    805  {
    806    using namespace mozilla::literals::ProportionValue_literals;
    807    STATIC_ASSERT_EQ(0_pc, ProportionValue(0.0));
    808    STATIC_ASSERT_EQ(0._pc, ProportionValue(0.0));
    809    STATIC_ASSERT_EQ(50_pc, ProportionValue(0.5));
    810    STATIC_ASSERT_EQ(50._pc, ProportionValue(0.5));
    811    STATIC_ASSERT_EQ(100_pc, ProportionValue(1.0));
    812    STATIC_ASSERT_EQ(100._pc, ProportionValue(1.0));
    813    STATIC_ASSERT_EQ(101_pc, ProportionValue(1.0));
    814    STATIC_ASSERT_EQ(100.01_pc, ProportionValue(1.0));
    815    STATIC_ASSERT_EQ(1000_pc, ProportionValue(1.0));
    816    STATIC_ASSERT_EQ(1000._pc, ProportionValue(1.0));
    817  }
    818  {
    819    // ProportionValue_literals is an inline namespace of mozilla::literals, so
    820    // it's optional.
    821    using namespace mozilla::literals;
    822    STATIC_ASSERT_EQ(0_pc, ProportionValue(0.0));
    823    STATIC_ASSERT_EQ(0._pc, ProportionValue(0.0));
    824    STATIC_ASSERT_EQ(50_pc, ProportionValue(0.5));
    825    STATIC_ASSERT_EQ(50._pc, ProportionValue(0.5));
    826    STATIC_ASSERT_EQ(100_pc, ProportionValue(1.0));
    827    STATIC_ASSERT_EQ(100._pc, ProportionValue(1.0));
    828    STATIC_ASSERT_EQ(101_pc, ProportionValue(1.0));
    829    STATIC_ASSERT_EQ(100.01_pc, ProportionValue(1.0));
    830    STATIC_ASSERT_EQ(1000_pc, ProportionValue(1.0));
    831    STATIC_ASSERT_EQ(1000._pc, ProportionValue(1.0));
    832  }
    833 
    834  // Invalid construction, conversion to double NaN.
    835  MOZ_RELEASE_ASSERT(std::isnan(ProportionValue::MakeInvalid().ToDouble()));
    836 
    837  using namespace mozilla::literals::ProportionValue_literals;
    838 
    839  // Conversion to&from underlying integral number.
    840  STATIC_ASSERT_EQ(
    841      ProportionValue::FromUnderlyingType((0_pc).ToUnderlyingType()).ToDouble(),
    842      0.0);
    843  STATIC_ASSERT_EQ(
    844      ProportionValue::FromUnderlyingType((50_pc).ToUnderlyingType())
    845          .ToDouble(),
    846      0.5);
    847  STATIC_ASSERT_EQ(
    848      ProportionValue::FromUnderlyingType((100_pc).ToUnderlyingType())
    849          .ToDouble(),
    850      1.0);
    851  STATIC_ASSERT(ProportionValue::FromUnderlyingType(
    852                    ProportionValue::MakeInvalid().ToUnderlyingType())
    853                    .IsInvalid());
    854 
    855  // IsExactlyZero.
    856  STATIC_ASSERT(ProportionValue().IsExactlyZero());
    857  STATIC_ASSERT((0_pc).IsExactlyZero());
    858  STATIC_ASSERT(!(50_pc).IsExactlyZero());
    859  STATIC_ASSERT(!(100_pc).IsExactlyZero());
    860  STATIC_ASSERT(!ProportionValue::MakeInvalid().IsExactlyZero());
    861 
    862  // IsExactlyOne.
    863  STATIC_ASSERT(!ProportionValue().IsExactlyOne());
    864  STATIC_ASSERT(!(0_pc).IsExactlyOne());
    865  STATIC_ASSERT(!(50_pc).IsExactlyOne());
    866  STATIC_ASSERT((100_pc).IsExactlyOne());
    867  STATIC_ASSERT(!ProportionValue::MakeInvalid().IsExactlyOne());
    868 
    869  // IsValid.
    870  STATIC_ASSERT(ProportionValue().IsValid());
    871  STATIC_ASSERT((0_pc).IsValid());
    872  STATIC_ASSERT((50_pc).IsValid());
    873  STATIC_ASSERT((100_pc).IsValid());
    874  STATIC_ASSERT(!ProportionValue::MakeInvalid().IsValid());
    875 
    876  // IsInvalid.
    877  STATIC_ASSERT(!ProportionValue().IsInvalid());
    878  STATIC_ASSERT(!(0_pc).IsInvalid());
    879  STATIC_ASSERT(!(50_pc).IsInvalid());
    880  STATIC_ASSERT(!(100_pc).IsInvalid());
    881  STATIC_ASSERT(ProportionValue::MakeInvalid().IsInvalid());
    882 
    883  // Addition.
    884  STATIC_ASSERT_EQ((0_pc + 0_pc).ToDouble(), 0.0);
    885  STATIC_ASSERT_EQ((0_pc + 100_pc).ToDouble(), 1.0);
    886  STATIC_ASSERT_EQ((100_pc + 0_pc).ToDouble(), 1.0);
    887  STATIC_ASSERT_EQ((100_pc + 100_pc).ToDouble(), 1.0);
    888  STATIC_ASSERT((ProportionValue::MakeInvalid() + 50_pc).IsInvalid());
    889  STATIC_ASSERT((50_pc + ProportionValue::MakeInvalid()).IsInvalid());
    890 
    891  // Subtraction.
    892  STATIC_ASSERT_EQ((0_pc - 0_pc).ToDouble(), 0.0);
    893  STATIC_ASSERT_EQ((0_pc - 100_pc).ToDouble(), 0.0);
    894  STATIC_ASSERT_EQ((100_pc - 0_pc).ToDouble(), 1.0);
    895  STATIC_ASSERT_EQ((100_pc - 100_pc).ToDouble(), 0.0);
    896  STATIC_ASSERT((ProportionValue::MakeInvalid() - 50_pc).IsInvalid());
    897  STATIC_ASSERT((50_pc - ProportionValue::MakeInvalid()).IsInvalid());
    898 
    899  // Multiplication.
    900  STATIC_ASSERT_EQ((0_pc * 0_pc).ToDouble(), 0.0);
    901  STATIC_ASSERT_EQ((0_pc * 100_pc).ToDouble(), 0.0);
    902  STATIC_ASSERT_EQ((50_pc * 50_pc).ToDouble(), 0.25);
    903  STATIC_ASSERT_EQ((50_pc * 100_pc).ToDouble(), 0.5);
    904  STATIC_ASSERT_EQ((100_pc * 50_pc).ToDouble(), 0.5);
    905  STATIC_ASSERT_EQ((100_pc * 0_pc).ToDouble(), 0.0);
    906  STATIC_ASSERT_EQ((100_pc * 100_pc).ToDouble(), 1.0);
    907  STATIC_ASSERT((ProportionValue::MakeInvalid() * 50_pc).IsInvalid());
    908  STATIC_ASSERT((50_pc * ProportionValue::MakeInvalid()).IsInvalid());
    909 
    910  // Division by a positive integer value.
    911  STATIC_ASSERT_EQ((100_pc / 1u).ToDouble(), 1.0);
    912  STATIC_ASSERT_EQ((100_pc / 2u).ToDouble(), 0.5);
    913  STATIC_ASSERT_EQ(
    914      (ProportionValue::FromUnderlyingType(6u) / 2u).ToUnderlyingType(), 3u);
    915  STATIC_ASSERT_EQ(
    916      (ProportionValue::FromUnderlyingType(5u) / 2u).ToUnderlyingType(), 2u);
    917  STATIC_ASSERT_EQ(
    918      (ProportionValue::FromUnderlyingType(1u) / 2u).ToUnderlyingType(), 0u);
    919  STATIC_ASSERT_EQ(
    920      (ProportionValue::FromUnderlyingType(0u) / 2u).ToUnderlyingType(), 0u);
    921  STATIC_ASSERT((100_pc / 0u).IsInvalid());
    922  STATIC_ASSERT((ProportionValue::MakeInvalid() / 2u).IsInvalid());
    923 
    924  // Multiplication by a positive integer value.
    925  STATIC_ASSERT_EQ((100_pc * 1u).ToDouble(), 1.0);
    926  STATIC_ASSERT_EQ((50_pc * 1u).ToDouble(), 0.5);
    927  STATIC_ASSERT_EQ((50_pc * 2u).ToDouble(), 1.0);
    928  STATIC_ASSERT_EQ((50_pc * 3u).ToDouble(), 1.0);  // Clamped.
    929  STATIC_ASSERT_EQ(
    930      (ProportionValue::FromUnderlyingType(1u) * 2u).ToUnderlyingType(), 2u);
    931  STATIC_ASSERT((ProportionValue::MakeInvalid() * 2u).IsInvalid());
    932 
    933  // Verifying PV - u < (PV / u) * u <= PV, with n=3, PV between 6 and 9 :
    934  STATIC_ASSERT_EQ(
    935      (ProportionValue::FromUnderlyingType(6u) / 3u).ToUnderlyingType(), 2u);
    936  STATIC_ASSERT_EQ(
    937      (ProportionValue::FromUnderlyingType(7u) / 3u).ToUnderlyingType(), 2u);
    938  STATIC_ASSERT_EQ(
    939      (ProportionValue::FromUnderlyingType(8u) / 3u).ToUnderlyingType(), 2u);
    940  STATIC_ASSERT_EQ(
    941      (ProportionValue::FromUnderlyingType(9u) / 3u).ToUnderlyingType(), 3u);
    942 
    943  // Direct comparisons.
    944  STATIC_ASSERT_EQ(0_pc, 0_pc);
    945  STATIC_ASSERT(0_pc == 0_pc);
    946  STATIC_ASSERT(!(0_pc == 100_pc));
    947  STATIC_ASSERT(0_pc != 100_pc);
    948  STATIC_ASSERT(!(0_pc != 0_pc));
    949  STATIC_ASSERT(0_pc < 100_pc);
    950  STATIC_ASSERT(!(0_pc < 0_pc));
    951  STATIC_ASSERT(0_pc <= 0_pc);
    952  STATIC_ASSERT(0_pc <= 100_pc);
    953  STATIC_ASSERT(!(100_pc <= 0_pc));
    954  STATIC_ASSERT(100_pc > 0_pc);
    955  STATIC_ASSERT(!(100_pc > 100_pc));
    956  STATIC_ASSERT(100_pc >= 0_pc);
    957  STATIC_ASSERT(100_pc >= 100_pc);
    958  STATIC_ASSERT(!(0_pc >= 100_pc));
    959  // 0.5 is binary-friendly, so we can double it and compare it exactly.
    960  STATIC_ASSERT_EQ(50_pc + 50_pc, 100_pc);
    961 
    962 #undef STATIC_ASSERT_EQ
    963 
    964  printf("TestProportionValue done\n");
    965 }
    966 
    967 template <typename Arg0, typename... Args>
    968 bool AreAllEqual(Arg0&& aArg0, Args&&... aArgs) {
    969  return ((aArg0 == aArgs) && ...);
    970 }
    971 
    972 void TestProgressLogger() {
    973  printf("TestProgressLogger...\n");
    974 
    975  using mozilla::ProgressLogger;
    976  using mozilla::ProportionValue;
    977  using namespace mozilla::literals::ProportionValue_literals;
    978 
    979  auto progressRefPtr = mozilla::MakeRefPtr<ProgressLogger::SharedProgress>();
    980  MOZ_RELEASE_ASSERT(progressRefPtr);
    981  MOZ_RELEASE_ASSERT(progressRefPtr->Progress().IsExactlyZero());
    982 
    983  {
    984    ProgressLogger pl(progressRefPtr, "Started", "All done");
    985    MOZ_RELEASE_ASSERT(progressRefPtr->Progress().IsExactlyZero());
    986    MOZ_RELEASE_ASSERT(pl.GetGlobalProgress().IsExactlyZero());
    987    MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->LastLocation(),
    988                                   pl.GetLastGlobalLocation(), "Started"));
    989 
    990    // At this top level, the scale is 1:1.
    991    pl.SetLocalProgress(10_pc, "Top 10%");
    992    MOZ_RELEASE_ASSERT(
    993        AreAllEqual(progressRefPtr->Progress(), pl.GetGlobalProgress(), 10_pc));
    994    MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->LastLocation(),
    995                                   pl.GetLastGlobalLocation(), "Top 10%"));
    996 
    997    pl.SetLocalProgress(0_pc, "Restarted");
    998    MOZ_RELEASE_ASSERT(
    999        AreAllEqual(progressRefPtr->Progress(), pl.GetGlobalProgress(), 0_pc));
   1000    MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->LastLocation(),
   1001                                   pl.GetLastGlobalLocation(), "Restarted"));
   1002 
   1003    {
   1004      // Create a sub-logger for the whole global range. Notice that this is
   1005      // moving the current progress back to 0.
   1006      ProgressLogger plSub1 =
   1007          pl.CreateSubLoggerFromTo(0_pc, "Sub1 started", 100_pc, "Sub1 ended");
   1008      MOZ_RELEASE_ASSERT(progressRefPtr->Progress().IsExactlyZero());
   1009      MOZ_RELEASE_ASSERT(pl.GetGlobalProgress().IsExactlyZero());
   1010      MOZ_RELEASE_ASSERT(plSub1.GetGlobalProgress().IsExactlyZero());
   1011      MOZ_RELEASE_ASSERT(AreAllEqual(
   1012          progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1013          plSub1.GetLastGlobalLocation(), "Sub1 started"));
   1014 
   1015      // At this level, the scale is still 1:1.
   1016      plSub1.SetLocalProgress(10_pc, "Sub1 10%");
   1017      MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->Progress(),
   1018                                     pl.GetGlobalProgress(),
   1019                                     plSub1.GetGlobalProgress(), 10_pc));
   1020      MOZ_RELEASE_ASSERT(AreAllEqual(
   1021          progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1022          plSub1.GetLastGlobalLocation(), "Sub1 10%"));
   1023 
   1024      {
   1025        // Create a sub-logger half the global range.
   1026        //   0              0.25   0.375    0.5    0.625    0.75             1
   1027        //   |---------------|-------|-------|-------|-------|---------------|
   1028        // plSub2:           0      0.25    0.5     0.75     1
   1029        ProgressLogger plSub2 = plSub1.CreateSubLoggerFromTo(
   1030            25_pc, "Sub2 started", 75_pc, "Sub2 ended");
   1031        MOZ_RELEASE_ASSERT(AreAllEqual(
   1032            progressRefPtr->Progress(), pl.GetGlobalProgress(),
   1033            plSub1.GetGlobalProgress(), plSub2.GetGlobalProgress(), 25_pc));
   1034        MOZ_RELEASE_ASSERT(AreAllEqual(
   1035            progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1036            plSub1.GetLastGlobalLocation(), plSub2.GetLastGlobalLocation(),
   1037            "Sub2 started"));
   1038 
   1039        plSub2.SetLocalProgress(25_pc, "Sub2 25%");
   1040        MOZ_RELEASE_ASSERT(AreAllEqual(
   1041            progressRefPtr->Progress(), pl.GetGlobalProgress(),
   1042            plSub1.GetGlobalProgress(), plSub2.GetGlobalProgress(), 37.5_pc));
   1043        MOZ_RELEASE_ASSERT(AreAllEqual(
   1044            progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1045            plSub1.GetLastGlobalLocation(), plSub2.GetLastGlobalLocation(),
   1046            "Sub2 25%"));
   1047 
   1048        plSub2.SetLocalProgress(50_pc, "Sub2 50%");
   1049        MOZ_RELEASE_ASSERT(AreAllEqual(
   1050            progressRefPtr->Progress(), pl.GetGlobalProgress(),
   1051            plSub1.GetGlobalProgress(), plSub2.GetGlobalProgress(), 50_pc));
   1052        MOZ_RELEASE_ASSERT(AreAllEqual(
   1053            progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1054            plSub1.GetLastGlobalLocation(), plSub2.GetLastGlobalLocation(),
   1055            "Sub2 50%"));
   1056 
   1057        {
   1058          // Create a sub-logger half the parent range.
   1059          //   0              0.25   0.375    0.5    0.625    0.75             1
   1060          //   |---------------|-------|-------|-------|-------|---------------|
   1061          // plSub2:           0      0.25    0.5     0.75     1
   1062          // plSub3:                           0      0.5      1
   1063          ProgressLogger plSub3 = plSub2.CreateSubLoggerTo(
   1064              "Sub3 started", 100_pc, ProgressLogger::NO_LOCATION_UPDATE);
   1065          MOZ_RELEASE_ASSERT(AreAllEqual(
   1066              progressRefPtr->Progress(), pl.GetGlobalProgress(),
   1067              plSub1.GetGlobalProgress(), plSub2.GetGlobalProgress(),
   1068              plSub3.GetGlobalProgress(), 50_pc));
   1069          MOZ_RELEASE_ASSERT(AreAllEqual(
   1070              progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1071              plSub1.GetLastGlobalLocation(), plSub2.GetLastGlobalLocation(),
   1072              plSub3.GetLastGlobalLocation(), "Sub3 started"));
   1073 
   1074          plSub3.SetLocalProgress(50_pc, "Sub3 50%");
   1075          MOZ_RELEASE_ASSERT(AreAllEqual(
   1076              progressRefPtr->Progress(), pl.GetGlobalProgress(),
   1077              plSub1.GetGlobalProgress(), plSub2.GetGlobalProgress(),
   1078              plSub3.GetGlobalProgress(), 62.5_pc));
   1079          MOZ_RELEASE_ASSERT(AreAllEqual(
   1080              progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1081              plSub1.GetLastGlobalLocation(), plSub2.GetLastGlobalLocation(),
   1082              plSub3.GetLastGlobalLocation(), "Sub3 50%"));
   1083        }  // End of plSub3
   1084 
   1085        // When plSub3 ends, progress moves to its 100%, which is also plSub2's
   1086        // 100%, which is plSub1's and the global progress of 75%
   1087        MOZ_RELEASE_ASSERT(AreAllEqual(
   1088            progressRefPtr->Progress(), pl.GetGlobalProgress(),
   1089            plSub1.GetGlobalProgress(), plSub2.GetGlobalProgress(), 75_pc));
   1090        // But location is still at the last explicit update.
   1091        MOZ_RELEASE_ASSERT(AreAllEqual(
   1092            progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1093            plSub1.GetLastGlobalLocation(), plSub2.GetLastGlobalLocation(),
   1094            "Sub3 50%"));
   1095      }  // End of plSub2
   1096 
   1097      MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->Progress(),
   1098                                     pl.GetGlobalProgress(),
   1099                                     plSub1.GetGlobalProgress(), 75_pc));
   1100      MOZ_RELEASE_ASSERT(AreAllEqual(
   1101          progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1102          plSub1.GetLastGlobalLocation(), "Sub2 ended"));
   1103    }  // End of plSub1
   1104 
   1105    MOZ_RELEASE_ASSERT(progressRefPtr->Progress().IsExactlyOne());
   1106    MOZ_RELEASE_ASSERT(pl.GetGlobalProgress().IsExactlyOne());
   1107    MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->LastLocation(),
   1108                                   pl.GetLastGlobalLocation(), "Sub1 ended"));
   1109 
   1110    const auto loopStart = 75_pc;
   1111    const auto loopEnd = 87.5_pc;
   1112    const uint32_t loopCount = 8;
   1113    uint32_t expectedIndex = 0u;
   1114    auto expectedIterationStart = loopStart;
   1115    const auto iterationIncrement = (loopEnd - loopStart) / loopCount;
   1116    for (auto&& [index, loopPL] : pl.CreateLoopSubLoggersFromTo(
   1117             loopStart, loopEnd, loopCount, "looping...")) {
   1118      MOZ_RELEASE_ASSERT(index == expectedIndex);
   1119      ++expectedIndex;
   1120      MOZ_RELEASE_ASSERT(
   1121          AreAllEqual(progressRefPtr->Progress(), pl.GetGlobalProgress(),
   1122                      loopPL.GetGlobalProgress(), expectedIterationStart));
   1123      MOZ_RELEASE_ASSERT(AreAllEqual(
   1124          progressRefPtr->LastLocation(), pl.GetLastGlobalLocation(),
   1125          loopPL.GetLastGlobalLocation(), "looping..."));
   1126 
   1127      loopPL.SetLocalProgress(50_pc, "half");
   1128      MOZ_RELEASE_ASSERT(loopPL.GetGlobalProgress() ==
   1129                         expectedIterationStart + iterationIncrement / 2u);
   1130      MOZ_RELEASE_ASSERT(
   1131          AreAllEqual(progressRefPtr->Progress(), pl.GetGlobalProgress(),
   1132                      loopPL.GetGlobalProgress(),
   1133                      expectedIterationStart + iterationIncrement / 2u));
   1134      MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->LastLocation(),
   1135                                     pl.GetLastGlobalLocation(),
   1136                                     loopPL.GetLastGlobalLocation(), "half"));
   1137 
   1138      expectedIterationStart = expectedIterationStart + iterationIncrement;
   1139    }
   1140    MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->Progress(),
   1141                                   pl.GetGlobalProgress(),
   1142                                   expectedIterationStart));
   1143    MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->LastLocation(),
   1144                                   pl.GetLastGlobalLocation(), "looping..."));
   1145  }  // End of pl
   1146  MOZ_RELEASE_ASSERT(progressRefPtr->Progress().IsExactlyOne());
   1147  MOZ_RELEASE_ASSERT(AreAllEqual(progressRefPtr->LastLocation(), "All done"));
   1148 
   1149  printf("TestProgressLogger done\n");
   1150 }
   1151 
   1152 #ifdef MOZ_GECKO_PROFILER
   1153 
   1154 [[maybe_unused]] static void SleepMilli(unsigned aMilliseconds) {
   1155 #  if defined(_MSC_VER) || defined(__MINGW32__)
   1156  Sleep(aMilliseconds);
   1157 #  else
   1158  struct timespec ts = {/* .tv_sec */ static_cast<time_t>(aMilliseconds / 1000),
   1159                        /* ts.tv_nsec */ long(aMilliseconds % 1000) * 1000000};
   1160  struct timespec tr = {0, 0};
   1161  while (nanosleep(&ts, &tr)) {
   1162    if (errno == EINTR) {
   1163      ts = tr;
   1164    } else {
   1165      printf("nanosleep() -> %s\n", strerror(errno));
   1166      exit(1);
   1167    }
   1168  }
   1169 #  endif
   1170 }
   1171 
   1172 [[maybe_unused]] static void WaitUntilTimeStampChanges(
   1173    const mozilla::TimeStamp& aTimeStampToCompare = mozilla::TimeStamp::Now()) {
   1174  while (aTimeStampToCompare == mozilla::TimeStamp::Now()) {
   1175    SleepMilli(1);
   1176  }
   1177 }
   1178 
   1179 using namespace mozilla;
   1180 
   1181 void TestPowerOfTwoMask() {
   1182  printf("TestPowerOfTwoMask...\n");
   1183 
   1184  static_assert(MakePowerOfTwoMask<uint32_t, 0>().MaskValue() == 0);
   1185  constexpr PowerOfTwoMask<uint32_t> c0 = MakePowerOfTwoMask<uint32_t, 0>();
   1186  MOZ_RELEASE_ASSERT(c0.MaskValue() == 0);
   1187 
   1188  static_assert(MakePowerOfTwoMask<uint32_t, 0xFFu>().MaskValue() == 0xFFu);
   1189  constexpr PowerOfTwoMask<uint32_t> cFF =
   1190      MakePowerOfTwoMask<uint32_t, 0xFFu>();
   1191  MOZ_RELEASE_ASSERT(cFF.MaskValue() == 0xFFu);
   1192 
   1193  static_assert(MakePowerOfTwoMask<uint32_t, 0xFFFFFFFFu>().MaskValue() ==
   1194                0xFFFFFFFFu);
   1195  constexpr PowerOfTwoMask<uint32_t> cFFFFFFFF =
   1196      MakePowerOfTwoMask<uint32_t, 0xFFFFFFFFu>();
   1197  MOZ_RELEASE_ASSERT(cFFFFFFFF.MaskValue() == 0xFFFFFFFFu);
   1198 
   1199  struct TestDataU32 {
   1200    uint32_t mInput;
   1201    uint32_t mMask;
   1202  };
   1203  // clang-format off
   1204  TestDataU32 tests[] = {
   1205    { 0, 0 },
   1206    { 1, 1 },
   1207    { 2, 3 },
   1208    { 3, 3 },
   1209    { 4, 7 },
   1210    { 5, 7 },
   1211    { (1u << 31) - 1, (1u << 31) - 1 },
   1212    { (1u << 31), uint32_t(-1) },
   1213    { (1u << 31) + 1, uint32_t(-1) },
   1214    { uint32_t(-1), uint32_t(-1) }
   1215  };
   1216  // clang-format on
   1217  for (const TestDataU32& test : tests) {
   1218    PowerOfTwoMask<uint32_t> p2m(test.mInput);
   1219    MOZ_RELEASE_ASSERT(p2m.MaskValue() == test.mMask);
   1220    for (const TestDataU32& inner : tests) {
   1221      if (p2m.MaskValue() != uint32_t(-1)) {
   1222        MOZ_RELEASE_ASSERT((inner.mInput % p2m) ==
   1223                           (inner.mInput % (p2m.MaskValue() + 1)));
   1224      }
   1225      MOZ_RELEASE_ASSERT((inner.mInput & p2m) == (inner.mInput % p2m));
   1226      MOZ_RELEASE_ASSERT((p2m & inner.mInput) == (inner.mInput & p2m));
   1227    }
   1228  }
   1229 
   1230  printf("TestPowerOfTwoMask done\n");
   1231 }
   1232 
   1233 void TestPowerOfTwo() {
   1234  printf("TestPowerOfTwo...\n");
   1235 
   1236  static_assert(MakePowerOfTwo<uint32_t, 1>().Value() == 1);
   1237  constexpr PowerOfTwo<uint32_t> c1 = MakePowerOfTwo<uint32_t, 1>();
   1238  MOZ_RELEASE_ASSERT(c1.Value() == 1);
   1239  static_assert(MakePowerOfTwo<uint32_t, 1>().Mask().MaskValue() == 0);
   1240 
   1241  static_assert(MakePowerOfTwo<uint32_t, 128>().Value() == 128);
   1242  constexpr PowerOfTwo<uint32_t> c128 = MakePowerOfTwo<uint32_t, 128>();
   1243  MOZ_RELEASE_ASSERT(c128.Value() == 128);
   1244  static_assert(MakePowerOfTwo<uint32_t, 128>().Mask().MaskValue() == 127);
   1245 
   1246  static_assert(MakePowerOfTwo<uint32_t, 0x80000000u>().Value() == 0x80000000u);
   1247  constexpr PowerOfTwo<uint32_t> cMax = MakePowerOfTwo<uint32_t, 0x80000000u>();
   1248  MOZ_RELEASE_ASSERT(cMax.Value() == 0x80000000u);
   1249  static_assert(MakePowerOfTwo<uint32_t, 0x80000000u>().Mask().MaskValue() ==
   1250                0x7FFFFFFFu);
   1251 
   1252  struct TestDataU32 {
   1253    uint32_t mInput;
   1254    uint32_t mValue;
   1255    uint32_t mMask;
   1256  };
   1257  // clang-format off
   1258  TestDataU32 tests[] = {
   1259    { 0, 1, 0 },
   1260    { 1, 1, 0 },
   1261    { 2, 2, 1 },
   1262    { 3, 4, 3 },
   1263    { 4, 4, 3 },
   1264    { 5, 8, 7 },
   1265    { (1u << 31) - 1, (1u << 31), (1u << 31) - 1 },
   1266    { (1u << 31), (1u << 31), (1u << 31) - 1 },
   1267    { (1u << 31) + 1, (1u << 31), (1u << 31) - 1 },
   1268    { uint32_t(-1), (1u << 31), (1u << 31) - 1 }
   1269  };
   1270  // clang-format on
   1271  for (const TestDataU32& test : tests) {
   1272    PowerOfTwo<uint32_t> p2(test.mInput);
   1273    MOZ_RELEASE_ASSERT(p2.Value() == test.mValue);
   1274    MOZ_RELEASE_ASSERT(p2.MaskValue() == test.mMask);
   1275    PowerOfTwoMask<uint32_t> p2m = p2.Mask();
   1276    MOZ_RELEASE_ASSERT(p2m.MaskValue() == test.mMask);
   1277    for (const TestDataU32& inner : tests) {
   1278      MOZ_RELEASE_ASSERT((inner.mInput % p2) == (inner.mInput % p2.Value()));
   1279    }
   1280  }
   1281 
   1282  printf("TestPowerOfTwo done\n");
   1283 }
   1284 
   1285 void TestLEB128() {
   1286  printf("TestLEB128...\n");
   1287 
   1288  MOZ_RELEASE_ASSERT(ULEB128MaxSize<uint8_t>() == 2);
   1289  MOZ_RELEASE_ASSERT(ULEB128MaxSize<uint16_t>() == 3);
   1290  MOZ_RELEASE_ASSERT(ULEB128MaxSize<uint32_t>() == 5);
   1291  MOZ_RELEASE_ASSERT(ULEB128MaxSize<uint64_t>() == 10);
   1292 
   1293  struct TestDataU64 {
   1294    uint64_t mValue;
   1295    unsigned mSize;
   1296    const char* mBytes;
   1297  };
   1298  // clang-format off
   1299  TestDataU64 tests[] = {
   1300    // Small numbers should keep their normal byte representation.
   1301    {                  0u,  1, "\0" },
   1302    {                  1u,  1, "\x01" },
   1303 
   1304    // 0111 1111 (127, or 0x7F) is the highest number that fits into a single
   1305    // LEB128 byte. It gets encoded as 0111 1111, note the most significant bit
   1306    // is off.
   1307    {               0x7Fu,  1, "\x7F" },
   1308 
   1309    // Next number: 128, or 0x80.
   1310    //   Original data representation:  1000 0000
   1311    //     Broken up into groups of 7:         1  0000000
   1312    // Padded with 0 (msB) or 1 (lsB):  00000001 10000000
   1313    //            Byte representation:  0x01     0x80
   1314    //            Little endian order:  -> 0x80 0x01
   1315    {               0x80u,  2, "\x80\x01" },
   1316 
   1317    // Next: 129, or 0x81 (showing that we don't lose low bits.)
   1318    //   Original data representation:  1000 0001
   1319    //     Broken up into groups of 7:         1  0000001
   1320    // Padded with 0 (msB) or 1 (lsB):  00000001 10000001
   1321    //            Byte representation:  0x01     0x81
   1322    //            Little endian order:  -> 0x81 0x01
   1323    {               0x81u,  2, "\x81\x01" },
   1324 
   1325    // Highest 8-bit number: 255, or 0xFF.
   1326    //   Original data representation:  1111 1111
   1327    //     Broken up into groups of 7:         1  1111111
   1328    // Padded with 0 (msB) or 1 (lsB):  00000001 11111111
   1329    //            Byte representation:  0x01     0xFF
   1330    //            Little endian order:  -> 0xFF 0x01
   1331    {               0xFFu,  2, "\xFF\x01" },
   1332 
   1333    // Next: 256, or 0x100.
   1334    //   Original data representation:  1 0000 0000
   1335    //     Broken up into groups of 7:        10  0000000
   1336    // Padded with 0 (msB) or 1 (lsB):  00000010 10000000
   1337    //            Byte representation:  0x10     0x80
   1338    //            Little endian order:  -> 0x80 0x02
   1339    {              0x100u,  2, "\x80\x02" },
   1340 
   1341    // Highest 32-bit number: 0xFFFFFFFF (8 bytes, all bits set).
   1342    // Original: 1111 1111 1111 1111 1111 1111 1111 1111
   1343    // Groups:     1111  1111111  1111111  1111111  1111111
   1344    // Padded: 00001111 11111111 11111111 11111111 11111111
   1345    // Bytes:  0x0F     0xFF     0xFF     0xFF     0xFF
   1346    // Little Endian: -> 0xFF 0xFF 0xFF 0xFF 0x0F
   1347    {         0xFFFFFFFFu,  5, "\xFF\xFF\xFF\xFF\x0F" },
   1348 
   1349    // Highest 64-bit number: 0xFFFFFFFFFFFFFFFF (16 bytes, all bits set).
   1350    // 64 bits, that's 9 groups of 7 bits, plus 1 (most significant) bit.
   1351    { 0xFFFFFFFFFFFFFFFFu, 10, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01" }
   1352  };
   1353  // clang-format on
   1354 
   1355  for (const TestDataU64& test : tests) {
   1356    MOZ_RELEASE_ASSERT(ULEB128Size(test.mValue) == test.mSize);
   1357    // Prepare a buffer that can accomodate the largest-possible LEB128.
   1358    uint8_t buffer[ULEB128MaxSize<uint64_t>()];
   1359    // Use a pointer into the buffer as iterator.
   1360    uint8_t* p = buffer;
   1361    // And write the LEB128.
   1362    WriteULEB128(test.mValue, p);
   1363    // Pointer (iterator) should have advanced just past the expected LEB128
   1364    // size.
   1365    MOZ_RELEASE_ASSERT(p == buffer + test.mSize);
   1366    // Check expected bytes.
   1367    for (unsigned i = 0; i < test.mSize; ++i) {
   1368      MOZ_RELEASE_ASSERT(buffer[i] == uint8_t(test.mBytes[i]));
   1369    }
   1370 
   1371    // Move pointer (iterator) back to start of buffer.
   1372    p = buffer;
   1373    // And read the LEB128 we wrote above.
   1374    uint64_t read = ReadULEB128<uint64_t>(p);
   1375    // Pointer (iterator) should have also advanced just past the expected
   1376    // LEB128 size.
   1377    MOZ_RELEASE_ASSERT(p == buffer + test.mSize);
   1378    // And check the read value.
   1379    MOZ_RELEASE_ASSERT(read == test.mValue);
   1380 
   1381    // Testing ULEB128 reader.
   1382    ULEB128Reader<uint64_t> reader;
   1383    MOZ_RELEASE_ASSERT(!reader.IsComplete());
   1384    // Move pointer back to start of buffer.
   1385    p = buffer;
   1386    for (;;) {
   1387      // Read a byte and feed it to the reader.
   1388      if (reader.FeedByteIsComplete(*p++)) {
   1389        break;
   1390      }
   1391      // Not complete yet, we shouldn't have reached the end pointer.
   1392      MOZ_RELEASE_ASSERT(!reader.IsComplete());
   1393      MOZ_RELEASE_ASSERT(p < buffer + test.mSize);
   1394    }
   1395    MOZ_RELEASE_ASSERT(reader.IsComplete());
   1396    // Pointer should have advanced just past the expected LEB128 size.
   1397    MOZ_RELEASE_ASSERT(p == buffer + test.mSize);
   1398    // And check the read value.
   1399    MOZ_RELEASE_ASSERT(reader.Value() == test.mValue);
   1400 
   1401    // And again after a Reset.
   1402    reader.Reset();
   1403    MOZ_RELEASE_ASSERT(!reader.IsComplete());
   1404    p = buffer;
   1405    for (;;) {
   1406      if (reader.FeedByteIsComplete(*p++)) {
   1407        break;
   1408      }
   1409      MOZ_RELEASE_ASSERT(!reader.IsComplete());
   1410      MOZ_RELEASE_ASSERT(p < buffer + test.mSize);
   1411    }
   1412    MOZ_RELEASE_ASSERT(reader.IsComplete());
   1413    MOZ_RELEASE_ASSERT(p == buffer + test.mSize);
   1414    MOZ_RELEASE_ASSERT(reader.Value() == test.mValue);
   1415  }
   1416 
   1417  printf("TestLEB128 done\n");
   1418 }
   1419 
   1420 struct StringWriteFunc final : public JSONWriteFunc {
   1421  std::string mString;
   1422 
   1423  void Write(const mozilla::Span<const char>& aStr) final {
   1424    mString.append(aStr.data(), aStr.size());
   1425  }
   1426 };
   1427 
   1428 void CheckJSON(mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
   1429               const char* aExpected, int aLine) {
   1430  const std::string& actual =
   1431      static_cast<StringWriteFunc&>(aWriter.WriteFunc()).mString;
   1432  if (strcmp(aExpected, actual.c_str()) != 0) {
   1433    fprintf(stderr,
   1434            "---- EXPECTED ---- (line %d)\n<<<%s>>>\n"
   1435            "---- ACTUAL ----\n<<<%s>>>\n",
   1436            aLine, aExpected, actual.c_str());
   1437    MOZ_RELEASE_ASSERT(false, "expected and actual output don't match");
   1438  }
   1439 }
   1440 
   1441 void TestJSONTimeOutput() {
   1442  printf("TestJSONTimeOutput...\n");
   1443 
   1444 #  define TEST(in, out)                                     \
   1445    do {                                                    \
   1446      mozilla::baseprofiler::SpliceableJSONWriter writer(   \
   1447          mozilla::MakeUnique<StringWriteFunc>(),           \
   1448          FailureLatchInfallibleSource::Singleton());       \
   1449      writer.Start();                                       \
   1450      writer.TimeDoubleMsProperty("time_ms", (in));         \
   1451      writer.End();                                         \
   1452      CheckJSON(writer, "{\"time_ms\":" out "}", __LINE__); \
   1453    } while (false);
   1454 
   1455  TEST(0, "0");
   1456 
   1457  TEST(0.000'000'1, "0");
   1458  TEST(0.000'000'4, "0");
   1459  TEST(0.000'000'499, "0");
   1460  TEST(0.000'000'5, "0.000001");
   1461  TEST(0.000'001, "0.000001");
   1462  TEST(0.000'01, "0.00001");
   1463  TEST(0.000'1, "0.0001");
   1464  TEST(0.001, "0.001");
   1465  TEST(0.01, "0.01");
   1466  TEST(0.1, "0.1");
   1467  TEST(1, "1");
   1468  TEST(2, "2");
   1469  TEST(10, "10");
   1470  TEST(100, "100");
   1471  TEST(1'000, "1000");
   1472  TEST(10'000, "10000");
   1473  TEST(100'000, "100000");
   1474  TEST(1'000'000, "1000000");
   1475  // 2^53-2 ns in ms. 2^53-1 is the highest integer value representable in
   1476  // double, -1 again because we're adding 0.5 before truncating.
   1477  // That's 104 days, after which the nanosecond precision would decrease.
   1478  TEST(9'007'199'254.740'990, "9007199254.74099");
   1479 
   1480  TEST(-0.000'000'1, "0");
   1481  TEST(-0.000'000'4, "0");
   1482  TEST(-0.000'000'499, "0");
   1483  TEST(-0.000'000'5, "-0.000001");
   1484  TEST(-0.000'001, "-0.000001");
   1485  TEST(-0.000'01, "-0.00001");
   1486  TEST(-0.000'1, "-0.0001");
   1487  TEST(-0.001, "-0.001");
   1488  TEST(-0.01, "-0.01");
   1489  TEST(-0.1, "-0.1");
   1490  TEST(-1, "-1");
   1491  TEST(-2, "-2");
   1492  TEST(-10, "-10");
   1493  TEST(-100, "-100");
   1494  TEST(-1'000, "-1000");
   1495  TEST(-10'000, "-10000");
   1496  TEST(-100'000, "-100000");
   1497  TEST(-1'000'000, "-1000000");
   1498  TEST(-9'007'199'254.740'990, "-9007199254.74099");
   1499 
   1500 #  undef TEST
   1501 
   1502  printf("TestJSONTimeOutput done\n");
   1503 }
   1504 
   1505 template <uint8_t byte, uint8_t... tail>
   1506 constexpr bool TestConstexprULEB128Reader(ULEB128Reader<uint64_t>& aReader) {
   1507  if (aReader.IsComplete()) {
   1508    return false;
   1509  }
   1510  const bool isComplete = aReader.FeedByteIsComplete(byte);
   1511  if (aReader.IsComplete() != isComplete) {
   1512    return false;
   1513  }
   1514  if constexpr (sizeof...(tail) == 0) {
   1515    return isComplete;
   1516  } else {
   1517    if (isComplete) {
   1518      return false;
   1519    }
   1520    return TestConstexprULEB128Reader<tail...>(aReader);
   1521  }
   1522 }
   1523 
   1524 template <uint64_t expected, uint8_t... bytes>
   1525 constexpr bool TestConstexprULEB128Reader() {
   1526  ULEB128Reader<uint64_t> reader;
   1527  if (!TestConstexprULEB128Reader<bytes...>(reader)) {
   1528    return false;
   1529  }
   1530  if (!reader.IsComplete()) {
   1531    return false;
   1532  }
   1533  if (reader.Value() != expected) {
   1534    return false;
   1535  }
   1536 
   1537  reader.Reset();
   1538  if (!TestConstexprULEB128Reader<bytes...>(reader)) {
   1539    return false;
   1540  }
   1541  if (!reader.IsComplete()) {
   1542    return false;
   1543  }
   1544  if (reader.Value() != expected) {
   1545    return false;
   1546  }
   1547 
   1548  return true;
   1549 }
   1550 
   1551 static_assert(TestConstexprULEB128Reader<0x0u, 0x0u>());
   1552 static_assert(!TestConstexprULEB128Reader<0x0u, 0x0u, 0x0u>());
   1553 static_assert(TestConstexprULEB128Reader<0x1u, 0x1u>());
   1554 static_assert(TestConstexprULEB128Reader<0x7Fu, 0x7Fu>());
   1555 static_assert(TestConstexprULEB128Reader<0x80u, 0x80u, 0x01u>());
   1556 static_assert(!TestConstexprULEB128Reader<0x80u, 0x80u>());
   1557 static_assert(!TestConstexprULEB128Reader<0x80u, 0x01u>());
   1558 static_assert(TestConstexprULEB128Reader<0x81u, 0x81u, 0x01u>());
   1559 static_assert(TestConstexprULEB128Reader<0xFFu, 0xFFu, 0x01u>());
   1560 static_assert(TestConstexprULEB128Reader<0x100u, 0x80u, 0x02u>());
   1561 static_assert(TestConstexprULEB128Reader<0xFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu,
   1562                                         0xFFu, 0x0Fu>());
   1563 static_assert(
   1564    !TestConstexprULEB128Reader<0xFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu>());
   1565 static_assert(!TestConstexprULEB128Reader<0xFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu,
   1566                                          0xFFu, 0xFFu, 0x0Fu>());
   1567 static_assert(
   1568    TestConstexprULEB128Reader<0xFFFFFFFFFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,
   1569                               0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0x01u>());
   1570 static_assert(
   1571    !TestConstexprULEB128Reader<0xFFFFFFFFFFFFFFFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,
   1572                                0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu>());
   1573 
   1574 static void TestChunk() {
   1575  printf("TestChunk...\n");
   1576 
   1577  static_assert(!std::is_default_constructible_v<ProfileBufferChunk>,
   1578                "ProfileBufferChunk should not be default-constructible");
   1579  static_assert(
   1580      !std::is_constructible_v<ProfileBufferChunk, ProfileBufferChunk::Length>,
   1581      "ProfileBufferChunk should not be constructible from Length");
   1582 
   1583  static_assert(
   1584      sizeof(ProfileBufferChunk::Header) ==
   1585          sizeof(ProfileBufferChunk::Header::mOffsetFirstBlock) +
   1586              sizeof(ProfileBufferChunk::Header::mOffsetPastLastBlock) +
   1587              sizeof(ProfileBufferChunk::Header::mStartTimeStamp) +
   1588              sizeof(ProfileBufferChunk::Header::mDoneTimeStamp) +
   1589              sizeof(ProfileBufferChunk::Header::mBufferBytes) +
   1590              sizeof(ProfileBufferChunk::Header::mBlockCount) +
   1591              sizeof(ProfileBufferChunk::Header::mRangeStart) +
   1592              sizeof(ProfileBufferChunk::Header::mProcessId) +
   1593              sizeof(ProfileBufferChunk::Header::mPADDING),
   1594      "ProfileBufferChunk::Header may have unwanted padding, please review");
   1595  // Note: The above static_assert is an attempt at keeping
   1596  // ProfileBufferChunk::Header tightly packed, but some changes could make this
   1597  // impossible to achieve (most probably due to alignment) -- Just do your
   1598  // best!
   1599 
   1600  constexpr ProfileBufferChunk::Length TestLen = 1000;
   1601 
   1602  // Basic allocations of different sizes.
   1603  for (ProfileBufferChunk::Length len = 0; len <= TestLen; ++len) {
   1604    auto chunk = ProfileBufferChunk::Create(len);
   1605    static_assert(
   1606        std::is_same_v<decltype(chunk), UniquePtr<ProfileBufferChunk>>,
   1607        "ProfileBufferChunk::Create() should return a "
   1608        "UniquePtr<ProfileBufferChunk>");
   1609    MOZ_RELEASE_ASSERT(!!chunk, "OOM!?");
   1610    MOZ_RELEASE_ASSERT(chunk->BufferBytes() >= len);
   1611    MOZ_RELEASE_ASSERT(chunk->ChunkBytes() >=
   1612                       len + ProfileBufferChunk::SizeofChunkMetadata());
   1613    MOZ_RELEASE_ASSERT(chunk->RemainingBytes() == chunk->BufferBytes());
   1614    MOZ_RELEASE_ASSERT(chunk->OffsetFirstBlock() == 0);
   1615    MOZ_RELEASE_ASSERT(chunk->OffsetPastLastBlock() == 0);
   1616    MOZ_RELEASE_ASSERT(chunk->BlockCount() == 0);
   1617    MOZ_RELEASE_ASSERT(chunk->ProcessId() == 0);
   1618    MOZ_RELEASE_ASSERT(chunk->RangeStart() == 0);
   1619    MOZ_RELEASE_ASSERT(chunk->BufferSpan().LengthBytes() ==
   1620                       chunk->BufferBytes());
   1621    MOZ_RELEASE_ASSERT(!chunk->GetNext());
   1622    MOZ_RELEASE_ASSERT(!chunk->ReleaseNext());
   1623    MOZ_RELEASE_ASSERT(chunk->Last() == chunk.get());
   1624  }
   1625 
   1626  // Allocate the main test Chunk.
   1627  auto chunkA = ProfileBufferChunk::Create(TestLen);
   1628  MOZ_RELEASE_ASSERT(!!chunkA, "OOM!?");
   1629  MOZ_RELEASE_ASSERT(chunkA->BufferBytes() >= TestLen);
   1630  MOZ_RELEASE_ASSERT(chunkA->ChunkBytes() >=
   1631                     TestLen + ProfileBufferChunk::SizeofChunkMetadata());
   1632  MOZ_RELEASE_ASSERT(!chunkA->GetNext());
   1633  MOZ_RELEASE_ASSERT(!chunkA->ReleaseNext());
   1634 
   1635  constexpr ProfileBufferIndex chunkARangeStart = 12345;
   1636  chunkA->SetRangeStart(chunkARangeStart);
   1637  MOZ_RELEASE_ASSERT(chunkA->RangeStart() == chunkARangeStart);
   1638 
   1639  // Get a read-only span over its buffer.
   1640  auto bufferA = chunkA->BufferSpan();
   1641  static_assert(
   1642      std::is_same_v<decltype(bufferA), Span<const ProfileBufferChunk::Byte>>,
   1643      "BufferSpan() should return a Span<const Byte>");
   1644  MOZ_RELEASE_ASSERT(bufferA.LengthBytes() == chunkA->BufferBytes());
   1645 
   1646  // Add the initial tail block.
   1647  constexpr ProfileBufferChunk::Length initTailLen = 10;
   1648  auto initTail = chunkA->ReserveInitialBlockAsTail(initTailLen);
   1649  static_assert(
   1650      std::is_same_v<decltype(initTail), Span<ProfileBufferChunk::Byte>>,
   1651      "ReserveInitialBlockAsTail() should return a Span<Byte>");
   1652  MOZ_RELEASE_ASSERT(initTail.LengthBytes() == initTailLen);
   1653  MOZ_RELEASE_ASSERT(initTail.Elements() == bufferA.Elements());
   1654  MOZ_RELEASE_ASSERT(chunkA->OffsetFirstBlock() == initTailLen);
   1655  MOZ_RELEASE_ASSERT(chunkA->OffsetPastLastBlock() == initTailLen);
   1656 
   1657  // Add the first complete block.
   1658  constexpr ProfileBufferChunk::Length block1Len = 20;
   1659  auto block1 = chunkA->ReserveBlock(block1Len);
   1660  static_assert(
   1661      std::is_same_v<decltype(block1), ProfileBufferChunk::ReserveReturn>,
   1662      "ReserveBlock() should return a ReserveReturn");
   1663  MOZ_RELEASE_ASSERT(block1.mBlockRangeIndex.ConvertToProfileBufferIndex() ==
   1664                     chunkARangeStart + initTailLen);
   1665  MOZ_RELEASE_ASSERT(block1.mSpan.LengthBytes() == block1Len);
   1666  MOZ_RELEASE_ASSERT(block1.mSpan.Elements() ==
   1667                     bufferA.Elements() + initTailLen);
   1668  MOZ_RELEASE_ASSERT(chunkA->OffsetFirstBlock() == initTailLen);
   1669  MOZ_RELEASE_ASSERT(chunkA->OffsetPastLastBlock() == initTailLen + block1Len);
   1670  MOZ_RELEASE_ASSERT(chunkA->RemainingBytes() != 0);
   1671 
   1672  // Add another block to over-fill the ProfileBufferChunk.
   1673  const ProfileBufferChunk::Length remaining =
   1674      chunkA->BufferBytes() - (initTailLen + block1Len);
   1675  constexpr ProfileBufferChunk::Length overfill = 30;
   1676  const ProfileBufferChunk::Length block2Len = remaining + overfill;
   1677  ProfileBufferChunk::ReserveReturn block2 = chunkA->ReserveBlock(block2Len);
   1678  MOZ_RELEASE_ASSERT(block2.mBlockRangeIndex.ConvertToProfileBufferIndex() ==
   1679                     chunkARangeStart + initTailLen + block1Len);
   1680  MOZ_RELEASE_ASSERT(block2.mSpan.LengthBytes() == remaining);
   1681  MOZ_RELEASE_ASSERT(block2.mSpan.Elements() ==
   1682                     bufferA.Elements() + initTailLen + block1Len);
   1683  MOZ_RELEASE_ASSERT(chunkA->OffsetFirstBlock() == initTailLen);
   1684  MOZ_RELEASE_ASSERT(chunkA->OffsetPastLastBlock() == chunkA->BufferBytes());
   1685  MOZ_RELEASE_ASSERT(chunkA->RemainingBytes() == 0);
   1686 
   1687  // Block must be marked "done" before it can be recycled.
   1688  chunkA->MarkDone();
   1689 
   1690  // It must be marked "recycled" before data can be added to it again.
   1691  chunkA->MarkRecycled();
   1692 
   1693  // Add an empty initial tail block.
   1694  Span<ProfileBufferChunk::Byte> initTail2 =
   1695      chunkA->ReserveInitialBlockAsTail(0);
   1696  MOZ_RELEASE_ASSERT(initTail2.LengthBytes() == 0);
   1697  MOZ_RELEASE_ASSERT(initTail2.Elements() == bufferA.Elements());
   1698  MOZ_RELEASE_ASSERT(chunkA->OffsetFirstBlock() == 0);
   1699  MOZ_RELEASE_ASSERT(chunkA->OffsetPastLastBlock() == 0);
   1700 
   1701  // Block must be marked "done" before it can be destroyed.
   1702  chunkA->MarkDone();
   1703 
   1704  chunkA->SetProcessId(123);
   1705  MOZ_RELEASE_ASSERT(chunkA->ProcessId() == 123);
   1706 
   1707  printf("TestChunk done\n");
   1708 }
   1709 
   1710 static void TestChunkManagerSingle() {
   1711  printf("TestChunkManagerSingle...\n");
   1712 
   1713  // Construct a ProfileBufferChunkManagerSingle for one chunk of size >=1000.
   1714  constexpr ProfileBufferChunk::Length ChunkMinBufferBytes = 1000;
   1715  ProfileBufferChunkManagerSingle cms{ChunkMinBufferBytes};
   1716 
   1717  // Reference to base class, to exercize virtual methods.
   1718  ProfileBufferChunkManager& cm = cms;
   1719 
   1720 #  ifdef DEBUG
   1721  const char* chunkManagerRegisterer = "TestChunkManagerSingle";
   1722  cm.RegisteredWith(chunkManagerRegisterer);
   1723 #  endif  // DEBUG
   1724 
   1725  const auto maxTotalSize = cm.MaxTotalSize();
   1726  MOZ_RELEASE_ASSERT(maxTotalSize >= ChunkMinBufferBytes);
   1727 
   1728  cm.SetChunkDestroyedCallback([](const ProfileBufferChunk&) {
   1729    MOZ_RELEASE_ASSERT(
   1730        false,
   1731        "ProfileBufferChunkManagerSingle should never destroy its one chunk");
   1732  });
   1733 
   1734  UniquePtr<ProfileBufferChunk> extantReleasedChunks =
   1735      cm.GetExtantReleasedChunks();
   1736  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Unexpected released chunk(s)");
   1737 
   1738  // First request.
   1739  UniquePtr<ProfileBufferChunk> chunk = cm.GetChunk();
   1740  MOZ_RELEASE_ASSERT(!!chunk, "First chunk request should always work");
   1741  MOZ_RELEASE_ASSERT(chunk->BufferBytes() >= ChunkMinBufferBytes,
   1742                     "Unexpected chunk size");
   1743  MOZ_RELEASE_ASSERT(!chunk->GetNext(), "There should only be one chunk");
   1744 
   1745  // Keep address, for later checks.
   1746  const uintptr_t chunkAddress = reinterpret_cast<uintptr_t>(chunk.get());
   1747 
   1748  extantReleasedChunks = cm.GetExtantReleasedChunks();
   1749  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Unexpected released chunk(s)");
   1750 
   1751  // Second request.
   1752  MOZ_RELEASE_ASSERT(!cm.GetChunk(), "Second chunk request should always fail");
   1753 
   1754  extantReleasedChunks = cm.GetExtantReleasedChunks();
   1755  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Unexpected released chunk(s)");
   1756 
   1757  // Add some data to the chunk (to verify recycling later on).
   1758  MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetFirstBlock == 0);
   1759  MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetPastLastBlock == 0);
   1760  MOZ_RELEASE_ASSERT(chunk->RangeStart() == 0);
   1761  chunk->SetRangeStart(100);
   1762  MOZ_RELEASE_ASSERT(chunk->RangeStart() == 100);
   1763  (void)chunk->ReserveInitialBlockAsTail(1);
   1764  (void)chunk->ReserveBlock(2);
   1765  MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetFirstBlock == 1);
   1766  MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetPastLastBlock == 1 + 2);
   1767 
   1768  // Release the first chunk.
   1769  chunk->MarkDone();
   1770  cm.ReleaseChunk(std::move(chunk));
   1771  MOZ_RELEASE_ASSERT(!chunk, "chunk UniquePtr should have been moved-from");
   1772 
   1773  // Request after release.
   1774  MOZ_RELEASE_ASSERT(!cm.GetChunk(),
   1775                     "Chunk request after release should also fail");
   1776 
   1777  // Check released chunk.
   1778  extantReleasedChunks = cm.GetExtantReleasedChunks();
   1779  MOZ_RELEASE_ASSERT(!!extantReleasedChunks,
   1780                     "Could not retrieve released chunk");
   1781  MOZ_RELEASE_ASSERT(!extantReleasedChunks->GetNext(),
   1782                     "There should only be one released chunk");
   1783  MOZ_RELEASE_ASSERT(
   1784      reinterpret_cast<uintptr_t>(extantReleasedChunks.get()) == chunkAddress,
   1785      "Released chunk should be first requested one");
   1786 
   1787  MOZ_RELEASE_ASSERT(!cm.GetExtantReleasedChunks(),
   1788                     "Unexpected extra released chunk(s)");
   1789 
   1790  // Another request after release.
   1791  MOZ_RELEASE_ASSERT(!cm.GetChunk(),
   1792                     "Chunk request after release should also fail");
   1793 
   1794  MOZ_RELEASE_ASSERT(
   1795      cm.MaxTotalSize() == maxTotalSize,
   1796      "MaxTotalSize() should not change after requests&releases");
   1797 
   1798  // Reset the chunk manager. (Single-only non-virtual function.)
   1799  cms.Reset(std::move(extantReleasedChunks));
   1800  MOZ_RELEASE_ASSERT(!extantReleasedChunks,
   1801                     "Released chunk UniquePtr should have been moved-from");
   1802 
   1803  MOZ_RELEASE_ASSERT(
   1804      cm.MaxTotalSize() == maxTotalSize,
   1805      "MaxTotalSize() should not change when resetting with the same chunk");
   1806 
   1807  // 2nd round, first request. Theoretically async, but this implementation just
   1808  // immediately runs the callback.
   1809  bool ran = false;
   1810  cm.RequestChunk([&](UniquePtr<ProfileBufferChunk> aChunk) {
   1811    ran = true;
   1812    MOZ_RELEASE_ASSERT(!!aChunk);
   1813    chunk = std::move(aChunk);
   1814  });
   1815  MOZ_RELEASE_ASSERT(ran, "RequestChunk callback not called immediately");
   1816  ran = false;
   1817  cm.FulfillChunkRequests();
   1818  MOZ_RELEASE_ASSERT(!ran, "FulfillChunkRequests should not have any effects");
   1819  MOZ_RELEASE_ASSERT(!!chunk, "First chunk request should always work");
   1820  MOZ_RELEASE_ASSERT(chunk->BufferBytes() >= ChunkMinBufferBytes,
   1821                     "Unexpected chunk size");
   1822  MOZ_RELEASE_ASSERT(!chunk->GetNext(), "There should only be one chunk");
   1823  MOZ_RELEASE_ASSERT(reinterpret_cast<uintptr_t>(chunk.get()) == chunkAddress,
   1824                     "Requested chunk should be first requested one");
   1825  // Verify that chunk is empty and usable.
   1826  MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetFirstBlock == 0);
   1827  MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetPastLastBlock == 0);
   1828  MOZ_RELEASE_ASSERT(chunk->RangeStart() == 0);
   1829  chunk->SetRangeStart(200);
   1830  MOZ_RELEASE_ASSERT(chunk->RangeStart() == 200);
   1831  (void)chunk->ReserveInitialBlockAsTail(3);
   1832  (void)chunk->ReserveBlock(4);
   1833  MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetFirstBlock == 3);
   1834  MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetPastLastBlock == 3 + 4);
   1835 
   1836  // Second request.
   1837  ran = false;
   1838  cm.RequestChunk([&](UniquePtr<ProfileBufferChunk> aChunk) {
   1839    ran = true;
   1840    MOZ_RELEASE_ASSERT(!aChunk, "Second chunk request should always fail");
   1841  });
   1842  MOZ_RELEASE_ASSERT(ran, "RequestChunk callback not called");
   1843 
   1844  // This one does nothing.
   1845  cm.ForgetUnreleasedChunks();
   1846 
   1847  // Don't forget to mark chunk "Done" before letting it die.
   1848  chunk->MarkDone();
   1849  chunk = nullptr;
   1850 
   1851  // Create a tiny chunk and reset the chunk manager with it.
   1852  chunk = ProfileBufferChunk::Create(1);
   1853  MOZ_RELEASE_ASSERT(!!chunk);
   1854  auto tinyChunkSize = chunk->BufferBytes();
   1855  MOZ_RELEASE_ASSERT(tinyChunkSize >= 1);
   1856  MOZ_RELEASE_ASSERT(tinyChunkSize < ChunkMinBufferBytes);
   1857  MOZ_RELEASE_ASSERT(chunk->RangeStart() == 0);
   1858  chunk->SetRangeStart(300);
   1859  MOZ_RELEASE_ASSERT(chunk->RangeStart() == 300);
   1860  cms.Reset(std::move(chunk));
   1861  MOZ_RELEASE_ASSERT(!chunk, "chunk UniquePtr should have been moved-from");
   1862  MOZ_RELEASE_ASSERT(cm.MaxTotalSize() == tinyChunkSize,
   1863                     "MaxTotalSize() should match the new chunk size");
   1864  chunk = cm.GetChunk();
   1865  MOZ_RELEASE_ASSERT(chunk->RangeStart() == 0, "Got non-recycled chunk");
   1866 
   1867  // Enough testing! Clean-up.
   1868  (void)chunk->ReserveInitialBlockAsTail(0);
   1869  chunk->MarkDone();
   1870  cm.ForgetUnreleasedChunks();
   1871 
   1872 #  ifdef DEBUG
   1873  cm.DeregisteredFrom(chunkManagerRegisterer);
   1874 #  endif  // DEBUG
   1875 
   1876  printf("TestChunkManagerSingle done\n");
   1877 }
   1878 
   1879 static void TestChunkManagerWithLocalLimit() {
   1880  printf("TestChunkManagerWithLocalLimit...\n");
   1881 
   1882  // Construct a ProfileBufferChunkManagerWithLocalLimit with chunk of minimum
   1883  // size >=100, up to 1000 bytes.
   1884  constexpr ProfileBufferChunk::Length MaxTotalBytes = 1000;
   1885  constexpr ProfileBufferChunk::Length ChunkMinBufferBytes = 100;
   1886  ProfileBufferChunkManagerWithLocalLimit cmll{MaxTotalBytes,
   1887                                               ChunkMinBufferBytes};
   1888 
   1889  // Reference to base class, to exercize virtual methods.
   1890  ProfileBufferChunkManager& cm = cmll;
   1891 
   1892 #  ifdef DEBUG
   1893  const char* chunkManagerRegisterer = "TestChunkManagerWithLocalLimit";
   1894  cm.RegisteredWith(chunkManagerRegisterer);
   1895 #  endif  // DEBUG
   1896 
   1897  MOZ_RELEASE_ASSERT(cm.MaxTotalSize() == MaxTotalBytes,
   1898                     "Max total size should be exactly as given");
   1899 
   1900  unsigned destroyedChunks = 0;
   1901  unsigned destroyedBytes = 0;
   1902  cm.SetChunkDestroyedCallback([&](const ProfileBufferChunk& aChunks) {
   1903    for (const ProfileBufferChunk* chunk = &aChunks; chunk;
   1904         chunk = chunk->GetNext()) {
   1905      destroyedChunks += 1;
   1906      destroyedBytes += chunk->BufferBytes();
   1907    }
   1908  });
   1909 
   1910  UniquePtr<ProfileBufferChunk> extantReleasedChunks =
   1911      cm.GetExtantReleasedChunks();
   1912  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Unexpected released chunk(s)");
   1913 
   1914  // First request.
   1915  UniquePtr<ProfileBufferChunk> chunk = cm.GetChunk();
   1916  MOZ_RELEASE_ASSERT(!!chunk,
   1917                     "First chunk immediate request should always work");
   1918  const auto chunkActualBufferBytes = chunk->BufferBytes();
   1919  MOZ_RELEASE_ASSERT(chunkActualBufferBytes >= ChunkMinBufferBytes,
   1920                     "Unexpected chunk size");
   1921  MOZ_RELEASE_ASSERT(!chunk->GetNext(), "There should only be one chunk");
   1922 
   1923  // Keep address, for later checks.
   1924  const uintptr_t chunk1Address = reinterpret_cast<uintptr_t>(chunk.get());
   1925 
   1926  extantReleasedChunks = cm.GetExtantReleasedChunks();
   1927  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Unexpected released chunk(s)");
   1928 
   1929  // Verify that ReleaseChunk accepts zero chunks.
   1930  cm.ReleaseChunk(nullptr);
   1931  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Unexpected released chunk(s)");
   1932 
   1933  // For this test, we need to be able to get at least 2 chunks without hitting
   1934  // the limit. (If this failed, it wouldn't necessary be a problem with
   1935  // ProfileBufferChunkManagerWithLocalLimit, fiddle with constants at the top
   1936  // of this test.)
   1937  MOZ_RELEASE_ASSERT(chunkActualBufferBytes < 2 * MaxTotalBytes);
   1938 
   1939  unsigned chunk1ReuseCount = 0;
   1940 
   1941  // We will do enough loops to go through the maximum size a number of times.
   1942  const unsigned Rollovers = 3;
   1943  const unsigned Loops = Rollovers * MaxTotalBytes / chunkActualBufferBytes;
   1944  for (unsigned i = 0; i < Loops; ++i) {
   1945    // Add some data to the chunk.
   1946    MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetFirstBlock == 0);
   1947    MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetPastLastBlock == 0);
   1948    MOZ_RELEASE_ASSERT(chunk->RangeStart() == 0);
   1949    const ProfileBufferIndex index = 1 + i * chunkActualBufferBytes;
   1950    chunk->SetRangeStart(index);
   1951    MOZ_RELEASE_ASSERT(chunk->RangeStart() == index);
   1952    (void)chunk->ReserveInitialBlockAsTail(1);
   1953    (void)chunk->ReserveBlock(2);
   1954    MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetFirstBlock == 1);
   1955    MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mOffsetPastLastBlock == 1 + 2);
   1956 
   1957    // Request a new chunk.
   1958    bool ran = false;
   1959    UniquePtr<ProfileBufferChunk> newChunk;
   1960    cm.RequestChunk([&](UniquePtr<ProfileBufferChunk> aChunk) {
   1961      ran = true;
   1962      newChunk = std::move(aChunk);
   1963    });
   1964    MOZ_RELEASE_ASSERT(
   1965        !ran, "RequestChunk should not immediately fulfill the request");
   1966    cm.FulfillChunkRequests();
   1967    MOZ_RELEASE_ASSERT(ran, "FulfillChunkRequests should invoke the callback");
   1968    MOZ_RELEASE_ASSERT(!!newChunk, "Chunk request should always work");
   1969    MOZ_RELEASE_ASSERT(newChunk->BufferBytes() == chunkActualBufferBytes,
   1970                       "Unexpected chunk size");
   1971    MOZ_RELEASE_ASSERT(!newChunk->GetNext(), "There should only be one chunk");
   1972 
   1973    // Mark previous chunk done and release it.
   1974    WaitUntilTimeStampChanges();  // Force "done" timestamp to change.
   1975    chunk->MarkDone();
   1976    cm.ReleaseChunk(std::move(chunk));
   1977 
   1978    // And cycle to the new chunk.
   1979    chunk = std::move(newChunk);
   1980 
   1981    if (reinterpret_cast<uintptr_t>(chunk.get()) == chunk1Address) {
   1982      ++chunk1ReuseCount;
   1983    }
   1984  }
   1985 
   1986  // Expect all rollovers except 1 to destroy chunks.
   1987  MOZ_RELEASE_ASSERT(destroyedChunks >= (Rollovers - 1) * MaxTotalBytes /
   1988                                            chunkActualBufferBytes,
   1989                     "Not enough destroyed chunks");
   1990  MOZ_RELEASE_ASSERT(destroyedBytes == destroyedChunks * chunkActualBufferBytes,
   1991                     "Mismatched destroyed chunks and bytes");
   1992  MOZ_RELEASE_ASSERT(chunk1ReuseCount >= (Rollovers - 1),
   1993                     "Not enough reuse of the first chunks");
   1994 
   1995  // Check that chunk manager is reentrant from request callback.
   1996  bool ran = false;
   1997  bool ranInner = false;
   1998  UniquePtr<ProfileBufferChunk> newChunk;
   1999  cm.RequestChunk([&](UniquePtr<ProfileBufferChunk> aChunk) {
   2000    ran = true;
   2001    MOZ_RELEASE_ASSERT(!!aChunk, "Chunk request should always work");
   2002    (void)aChunk->ReserveInitialBlockAsTail(0);
   2003    WaitUntilTimeStampChanges();  // Force "done" timestamp to change.
   2004    aChunk->MarkDone();
   2005    UniquePtr<ProfileBufferChunk> anotherChunk = cm.GetChunk();
   2006    MOZ_RELEASE_ASSERT(!!anotherChunk);
   2007    (void)anotherChunk->ReserveInitialBlockAsTail(0);
   2008    WaitUntilTimeStampChanges();  // Force "done" timestamp to change.
   2009    anotherChunk->MarkDone();
   2010    cm.RequestChunk([&](UniquePtr<ProfileBufferChunk> aChunk) {
   2011      ranInner = true;
   2012      MOZ_RELEASE_ASSERT(!!aChunk, "Chunk request should always work");
   2013      (void)aChunk->ReserveInitialBlockAsTail(0);
   2014      WaitUntilTimeStampChanges();  // Force "done" timestamp to change.
   2015      aChunk->MarkDone();
   2016    });
   2017    MOZ_RELEASE_ASSERT(
   2018        !ranInner, "RequestChunk should not immediately fulfill the request");
   2019  });
   2020  MOZ_RELEASE_ASSERT(!ran,
   2021                     "RequestChunk should not immediately fulfill the request");
   2022  MOZ_RELEASE_ASSERT(
   2023      !ranInner,
   2024      "RequestChunk should not immediately fulfill the inner request");
   2025  cm.FulfillChunkRequests();
   2026  MOZ_RELEASE_ASSERT(ran, "FulfillChunkRequests should invoke the callback");
   2027  MOZ_RELEASE_ASSERT(!ranInner,
   2028                     "FulfillChunkRequests should not immediately fulfill "
   2029                     "the inner request");
   2030  cm.FulfillChunkRequests();
   2031  MOZ_RELEASE_ASSERT(
   2032      ran, "2nd FulfillChunkRequests should invoke the inner request callback");
   2033 
   2034  // Enough testing! Clean-up.
   2035  (void)chunk->ReserveInitialBlockAsTail(0);
   2036  WaitUntilTimeStampChanges();  // Force "done" timestamp to change.
   2037  chunk->MarkDone();
   2038  cm.ForgetUnreleasedChunks();
   2039 
   2040  // Special testing of the release algorithm, to make sure released chunks get
   2041  // sorted.
   2042  constexpr unsigned RandomReleaseChunkLoop = 100;
   2043  // Build a vector of chunks, and mark them "done", ready to be released.
   2044  Vector<UniquePtr<ProfileBufferChunk>> chunksToRelease;
   2045  MOZ_RELEASE_ASSERT(chunksToRelease.reserve(RandomReleaseChunkLoop));
   2046  Vector<TimeStamp> chunksTimeStamps;
   2047  MOZ_RELEASE_ASSERT(chunksTimeStamps.reserve(RandomReleaseChunkLoop));
   2048  for (unsigned i = 0; i < RandomReleaseChunkLoop; ++i) {
   2049    UniquePtr<ProfileBufferChunk> chunk = cm.GetChunk();
   2050    MOZ_RELEASE_ASSERT(chunk);
   2051    (void)chunk->ReserveInitialBlockAsTail(0);
   2052    chunk->MarkDone();
   2053    MOZ_RELEASE_ASSERT(!chunk->ChunkHeader().mDoneTimeStamp.IsNull());
   2054    chunksTimeStamps.infallibleEmplaceBack(chunk->ChunkHeader().mDoneTimeStamp);
   2055    chunksToRelease.infallibleEmplaceBack(std::move(chunk));
   2056    if (i % 10 == 0) {
   2057      // "Done" timestamps should *usually* increase, let's make extra sure some
   2058      // timestamps are actually different.
   2059      WaitUntilTimeStampChanges();
   2060    }
   2061  }
   2062  // Shuffle the list.
   2063  std::random_device randomDevice;
   2064  std::mt19937 generator(randomDevice());
   2065  std::shuffle(chunksToRelease.begin(), chunksToRelease.end(), generator);
   2066  // And release chunks one by one, checking that the list of released chunks
   2067  // is always sorted.
   2068  printf("TestChunkManagerWithLocalLimit - Shuffle test timestamps:");
   2069  for (unsigned i = 0; i < RandomReleaseChunkLoop; ++i) {
   2070    printf(" %f", (chunksToRelease[i]->ChunkHeader().mDoneTimeStamp -
   2071                   TimeStamp::ProcessCreation())
   2072                      .ToMicroseconds());
   2073    cm.ReleaseChunk(std::move(chunksToRelease[i]));
   2074    cm.PeekExtantReleasedChunks([i](const ProfileBufferChunk* releasedChunks) {
   2075      MOZ_RELEASE_ASSERT(releasedChunks);
   2076      unsigned releasedChunkCount = 1;
   2077      for (;;) {
   2078        const ProfileBufferChunk* nextChunk = releasedChunks->GetNext();
   2079        if (!nextChunk) {
   2080          break;
   2081        }
   2082        ++releasedChunkCount;
   2083        MOZ_RELEASE_ASSERT(releasedChunks->ChunkHeader().mDoneTimeStamp <=
   2084                           nextChunk->ChunkHeader().mDoneTimeStamp);
   2085        releasedChunks = nextChunk;
   2086      }
   2087      MOZ_RELEASE_ASSERT(releasedChunkCount == i + 1);
   2088    });
   2089  }
   2090  printf("\n");
   2091  // Finally, the whole list of released chunks should have the exact same
   2092  // timestamps as the initial list of "done" chunks.
   2093  extantReleasedChunks = cm.GetExtantReleasedChunks();
   2094  for (unsigned i = 0; i < RandomReleaseChunkLoop; ++i) {
   2095    MOZ_RELEASE_ASSERT(extantReleasedChunks, "Not enough released chunks");
   2096    MOZ_RELEASE_ASSERT(extantReleasedChunks->ChunkHeader().mDoneTimeStamp ==
   2097                       chunksTimeStamps[i]);
   2098    (void)std::exchange(extantReleasedChunks,
   2099                        extantReleasedChunks->ReleaseNext());
   2100  }
   2101  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Too many released chunks");
   2102 
   2103 #  ifdef DEBUG
   2104  cm.DeregisteredFrom(chunkManagerRegisterer);
   2105 #  endif  // DEBUG
   2106 
   2107  printf("TestChunkManagerWithLocalLimit done\n");
   2108 }
   2109 
   2110 static bool IsSameMetadata(
   2111    const ProfileBufferControlledChunkManager::ChunkMetadata& a1,
   2112    const ProfileBufferControlledChunkManager::ChunkMetadata& a2) {
   2113  return a1.mDoneTimeStamp == a2.mDoneTimeStamp &&
   2114         a1.mBufferBytes == a2.mBufferBytes;
   2115 };
   2116 
   2117 static bool IsSameUpdate(
   2118    const ProfileBufferControlledChunkManager::Update& a1,
   2119    const ProfileBufferControlledChunkManager::Update& a2) {
   2120  // Final and not-an-update don't carry other data, so we can test these two
   2121  // states first.
   2122  if (a1.IsFinal() || a2.IsFinal()) {
   2123    return a1.IsFinal() && a2.IsFinal();
   2124  }
   2125  if (a1.IsNotUpdate() || a2.IsNotUpdate()) {
   2126    return a1.IsNotUpdate() && a2.IsNotUpdate();
   2127  }
   2128 
   2129  // Here, both are "normal" udpates, check member variables:
   2130 
   2131  if (a1.UnreleasedBytes() != a2.UnreleasedBytes()) {
   2132    return false;
   2133  }
   2134  if (a1.ReleasedBytes() != a2.ReleasedBytes()) {
   2135    return false;
   2136  }
   2137  if (a1.OldestDoneTimeStamp() != a2.OldestDoneTimeStamp()) {
   2138    return false;
   2139  }
   2140  if (a1.NewlyReleasedChunksRef().size() !=
   2141      a2.NewlyReleasedChunksRef().size()) {
   2142    return false;
   2143  }
   2144  for (unsigned i = 0; i < a1.NewlyReleasedChunksRef().size(); ++i) {
   2145    if (!IsSameMetadata(a1.NewlyReleasedChunksRef()[i],
   2146                        a2.NewlyReleasedChunksRef()[i])) {
   2147      return false;
   2148    }
   2149  }
   2150  return true;
   2151 }
   2152 
   2153 static void TestControlledChunkManagerUpdate() {
   2154  printf("TestControlledChunkManagerUpdate...\n");
   2155 
   2156  using Update = ProfileBufferControlledChunkManager::Update;
   2157 
   2158  // Default construction.
   2159  Update update1;
   2160  MOZ_RELEASE_ASSERT(update1.IsNotUpdate());
   2161  MOZ_RELEASE_ASSERT(!update1.IsFinal());
   2162 
   2163  // Clear an already-cleared update.
   2164  update1.Clear();
   2165  MOZ_RELEASE_ASSERT(update1.IsNotUpdate());
   2166  MOZ_RELEASE_ASSERT(!update1.IsFinal());
   2167 
   2168  // Final construction with nullptr.
   2169  const Update final(nullptr);
   2170  MOZ_RELEASE_ASSERT(final.IsFinal());
   2171  MOZ_RELEASE_ASSERT(!final.IsNotUpdate());
   2172 
   2173  // Copy final to cleared.
   2174  update1 = final;
   2175  MOZ_RELEASE_ASSERT(update1.IsFinal());
   2176  MOZ_RELEASE_ASSERT(!update1.IsNotUpdate());
   2177 
   2178  // Copy final to final.
   2179  update1 = final;
   2180  MOZ_RELEASE_ASSERT(update1.IsFinal());
   2181  MOZ_RELEASE_ASSERT(!update1.IsNotUpdate());
   2182 
   2183  // Clear a final update.
   2184  update1.Clear();
   2185  MOZ_RELEASE_ASSERT(update1.IsNotUpdate());
   2186  MOZ_RELEASE_ASSERT(!update1.IsFinal());
   2187 
   2188  // Move final to cleared.
   2189  update1 = Update(nullptr);
   2190  MOZ_RELEASE_ASSERT(update1.IsFinal());
   2191  MOZ_RELEASE_ASSERT(!update1.IsNotUpdate());
   2192 
   2193  // Move final to final.
   2194  update1 = Update(nullptr);
   2195  MOZ_RELEASE_ASSERT(update1.IsFinal());
   2196  MOZ_RELEASE_ASSERT(!update1.IsNotUpdate());
   2197 
   2198  // Move from not-an-update (effectively same as Clear).
   2199  update1 = Update();
   2200  MOZ_RELEASE_ASSERT(update1.IsNotUpdate());
   2201  MOZ_RELEASE_ASSERT(!update1.IsFinal());
   2202 
   2203  auto CreateBiggerChunkAfter = [](const ProfileBufferChunk& aChunkToBeat) {
   2204    while (TimeStamp::Now() <= aChunkToBeat.ChunkHeader().mDoneTimeStamp) {
   2205      ::SleepMilli(1);
   2206    }
   2207    auto chunk = ProfileBufferChunk::Create(aChunkToBeat.BufferBytes() * 2);
   2208    MOZ_RELEASE_ASSERT(!!chunk);
   2209    MOZ_RELEASE_ASSERT(chunk->BufferBytes() >= aChunkToBeat.BufferBytes() * 2);
   2210    (void)chunk->ReserveInitialBlockAsTail(0);
   2211    chunk->MarkDone();
   2212    MOZ_RELEASE_ASSERT(chunk->ChunkHeader().mDoneTimeStamp >
   2213                       aChunkToBeat.ChunkHeader().mDoneTimeStamp);
   2214    return chunk;
   2215  };
   2216 
   2217  update1 = Update(1, 2, nullptr, nullptr);
   2218 
   2219  // Create initial update with 2 released chunks and 1 unreleased chunk.
   2220  auto released = ProfileBufferChunk::Create(10);
   2221  ProfileBufferChunk* c1 = released.get();
   2222  (void)c1->ReserveInitialBlockAsTail(0);
   2223  c1->MarkDone();
   2224 
   2225  released->SetLast(CreateBiggerChunkAfter(*c1));
   2226  ProfileBufferChunk* c2 = c1->GetNext();
   2227 
   2228  auto unreleased = CreateBiggerChunkAfter(*c2);
   2229  ProfileBufferChunk* c3 = unreleased.get();
   2230 
   2231  Update update2(c3->BufferBytes(), c1->BufferBytes() + c2->BufferBytes(), c1,
   2232                 c1);
   2233  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2234      update2,
   2235      Update(c3->BufferBytes(), c1->BufferBytes() + c2->BufferBytes(),
   2236             c1->ChunkHeader().mDoneTimeStamp,
   2237             {{c1->ChunkHeader().mDoneTimeStamp, c1->BufferBytes()},
   2238              {c2->ChunkHeader().mDoneTimeStamp, c2->BufferBytes()}})));
   2239  // Check every field, this time only, after that we'll trust that the
   2240  // `SameUpdate` test will be enough.
   2241  MOZ_RELEASE_ASSERT(!update2.IsNotUpdate());
   2242  MOZ_RELEASE_ASSERT(!update2.IsFinal());
   2243  MOZ_RELEASE_ASSERT(update2.UnreleasedBytes() == c3->BufferBytes());
   2244  MOZ_RELEASE_ASSERT(update2.ReleasedBytes() ==
   2245                     c1->BufferBytes() + c2->BufferBytes());
   2246  MOZ_RELEASE_ASSERT(update2.OldestDoneTimeStamp() ==
   2247                     c1->ChunkHeader().mDoneTimeStamp);
   2248  MOZ_RELEASE_ASSERT(update2.NewlyReleasedChunksRef().size() == 2);
   2249  MOZ_RELEASE_ASSERT(
   2250      IsSameMetadata(update2.NewlyReleasedChunksRef()[0],
   2251                     {c1->ChunkHeader().mDoneTimeStamp, c1->BufferBytes()}));
   2252  MOZ_RELEASE_ASSERT(
   2253      IsSameMetadata(update2.NewlyReleasedChunksRef()[1],
   2254                     {c2->ChunkHeader().mDoneTimeStamp, c2->BufferBytes()}));
   2255 
   2256  // Fold into not-an-update.
   2257  update1.Fold(std::move(update2));
   2258  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2259      update1,
   2260      Update(c3->BufferBytes(), c1->BufferBytes() + c2->BufferBytes(),
   2261             c1->ChunkHeader().mDoneTimeStamp,
   2262             {{c1->ChunkHeader().mDoneTimeStamp, c1->BufferBytes()},
   2263              {c2->ChunkHeader().mDoneTimeStamp, c2->BufferBytes()}})));
   2264 
   2265  // Pretend nothing happened.
   2266  update2 = Update(c3->BufferBytes(), c1->BufferBytes() + c2->BufferBytes(), c1,
   2267                   nullptr);
   2268  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2269      update2, Update(c3->BufferBytes(), c1->BufferBytes() + c2->BufferBytes(),
   2270                      c1->ChunkHeader().mDoneTimeStamp, {})));
   2271  update1.Fold(std::move(update2));
   2272  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2273      update1,
   2274      Update(c3->BufferBytes(), c1->BufferBytes() + c2->BufferBytes(),
   2275             c1->ChunkHeader().mDoneTimeStamp,
   2276             {{c1->ChunkHeader().mDoneTimeStamp, c1->BufferBytes()},
   2277              {c2->ChunkHeader().mDoneTimeStamp, c2->BufferBytes()}})));
   2278 
   2279  // Pretend there's a new unreleased chunk.
   2280  c3->SetLast(CreateBiggerChunkAfter(*c3));
   2281  ProfileBufferChunk* c4 = c3->GetNext();
   2282  update2 = Update(c3->BufferBytes() + c4->BufferBytes(),
   2283                   c1->BufferBytes() + c2->BufferBytes(), c1, nullptr);
   2284  MOZ_RELEASE_ASSERT(
   2285      IsSameUpdate(update2, Update(c3->BufferBytes() + c4->BufferBytes(),
   2286                                   c1->BufferBytes() + c2->BufferBytes(),
   2287                                   c1->ChunkHeader().mDoneTimeStamp, {})));
   2288  update1.Fold(std::move(update2));
   2289  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2290      update1,
   2291      Update(c3->BufferBytes() + c4->BufferBytes(),
   2292             c1->BufferBytes() + c2->BufferBytes(),
   2293             c1->ChunkHeader().mDoneTimeStamp,
   2294             {{c1->ChunkHeader().mDoneTimeStamp, c1->BufferBytes()},
   2295              {c2->ChunkHeader().mDoneTimeStamp, c2->BufferBytes()}})));
   2296 
   2297  // Pretend the first unreleased chunk c3 has been released.
   2298  released->SetLast(std::exchange(unreleased, unreleased->ReleaseNext()));
   2299  update2 =
   2300      Update(c4->BufferBytes(),
   2301             c1->BufferBytes() + c2->BufferBytes() + c3->BufferBytes(), c1, c3);
   2302  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2303      update2,
   2304      Update(c4->BufferBytes(),
   2305             c1->BufferBytes() + c2->BufferBytes() + c3->BufferBytes(),
   2306             c1->ChunkHeader().mDoneTimeStamp,
   2307             {{c3->ChunkHeader().mDoneTimeStamp, c3->BufferBytes()}})));
   2308  update1.Fold(std::move(update2));
   2309  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2310      update1,
   2311      Update(c4->BufferBytes(),
   2312             c1->BufferBytes() + c2->BufferBytes() + c3->BufferBytes(),
   2313             c1->ChunkHeader().mDoneTimeStamp,
   2314             {{c1->ChunkHeader().mDoneTimeStamp, c1->BufferBytes()},
   2315              {c2->ChunkHeader().mDoneTimeStamp, c2->BufferBytes()},
   2316              {c3->ChunkHeader().mDoneTimeStamp, c3->BufferBytes()}})));
   2317 
   2318  // Pretend c1 has been destroyed, so the oldest timestamp is now at c2.
   2319  released = released->ReleaseNext();
   2320  c1 = nullptr;
   2321  update2 = Update(c4->BufferBytes(), c2->BufferBytes() + c3->BufferBytes(), c2,
   2322                   nullptr);
   2323  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2324      update2, Update(c4->BufferBytes(), c2->BufferBytes() + c3->BufferBytes(),
   2325                      c2->ChunkHeader().mDoneTimeStamp, {})));
   2326  update1.Fold(std::move(update2));
   2327  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2328      update1,
   2329      Update(c4->BufferBytes(), c2->BufferBytes() + c3->BufferBytes(),
   2330             c2->ChunkHeader().mDoneTimeStamp,
   2331             {{c2->ChunkHeader().mDoneTimeStamp, c2->BufferBytes()},
   2332              {c3->ChunkHeader().mDoneTimeStamp, c3->BufferBytes()}})));
   2333 
   2334  // Pretend c2 has been recycled to make unreleased c5, and c4 has been
   2335  // released.
   2336  auto recycled = std::exchange(released, released->ReleaseNext());
   2337  recycled->MarkRecycled();
   2338  (void)recycled->ReserveInitialBlockAsTail(0);
   2339  recycled->MarkDone();
   2340  released->SetLast(std::move(unreleased));
   2341  unreleased = std::move(recycled);
   2342  ProfileBufferChunk* c5 = c2;
   2343  c2 = nullptr;
   2344  update2 =
   2345      Update(c5->BufferBytes(), c3->BufferBytes() + c4->BufferBytes(), c3, c4);
   2346  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2347      update2,
   2348      Update(c5->BufferBytes(), c3->BufferBytes() + c4->BufferBytes(),
   2349             c3->ChunkHeader().mDoneTimeStamp,
   2350             {{c4->ChunkHeader().mDoneTimeStamp, c4->BufferBytes()}})));
   2351  update1.Fold(std::move(update2));
   2352  MOZ_RELEASE_ASSERT(IsSameUpdate(
   2353      update1,
   2354      Update(c5->BufferBytes(), c3->BufferBytes() + c4->BufferBytes(),
   2355             c3->ChunkHeader().mDoneTimeStamp,
   2356             {{c3->ChunkHeader().mDoneTimeStamp, c3->BufferBytes()},
   2357              {c4->ChunkHeader().mDoneTimeStamp, c4->BufferBytes()}})));
   2358 
   2359  // And send a final update.
   2360  update1.Fold(Update(nullptr));
   2361  MOZ_RELEASE_ASSERT(update1.IsFinal());
   2362  MOZ_RELEASE_ASSERT(!update1.IsNotUpdate());
   2363 
   2364  printf("TestControlledChunkManagerUpdate done\n");
   2365 }
   2366 
   2367 static void TestControlledChunkManagerWithLocalLimit() {
   2368  printf("TestControlledChunkManagerWithLocalLimit...\n");
   2369 
   2370  // Construct a ProfileBufferChunkManagerWithLocalLimit with chunk of minimum
   2371  // size >=100, up to 1000 bytes.
   2372  constexpr ProfileBufferChunk::Length MaxTotalBytes = 1000;
   2373  constexpr ProfileBufferChunk::Length ChunkMinBufferBytes = 100;
   2374  ProfileBufferChunkManagerWithLocalLimit cmll{MaxTotalBytes,
   2375                                               ChunkMinBufferBytes};
   2376 
   2377  // Reference to chunk manager base class.
   2378  ProfileBufferChunkManager& cm = cmll;
   2379 
   2380  // Reference to controlled chunk manager base class.
   2381  ProfileBufferControlledChunkManager& ccm = cmll;
   2382 
   2383 #  ifdef DEBUG
   2384  const char* chunkManagerRegisterer =
   2385      "TestControlledChunkManagerWithLocalLimit";
   2386  cm.RegisteredWith(chunkManagerRegisterer);
   2387 #  endif  // DEBUG
   2388 
   2389  MOZ_RELEASE_ASSERT(cm.MaxTotalSize() == MaxTotalBytes,
   2390                     "Max total size should be exactly as given");
   2391 
   2392  unsigned destroyedChunks = 0;
   2393  unsigned destroyedBytes = 0;
   2394  cm.SetChunkDestroyedCallback([&](const ProfileBufferChunk& aChunks) {
   2395    for (const ProfileBufferChunk* chunk = &aChunks; chunk;
   2396         chunk = chunk->GetNext()) {
   2397      destroyedChunks += 1;
   2398      destroyedBytes += chunk->BufferBytes();
   2399    }
   2400  });
   2401 
   2402  using Update = ProfileBufferControlledChunkManager::Update;
   2403  unsigned updateCount = 0;
   2404  ProfileBufferControlledChunkManager::Update update;
   2405  MOZ_RELEASE_ASSERT(update.IsNotUpdate());
   2406  auto updateCallback = [&](Update&& aUpdate) {
   2407    ++updateCount;
   2408    update.Fold(std::move(aUpdate));
   2409  };
   2410  ccm.SetUpdateCallback(updateCallback);
   2411  MOZ_RELEASE_ASSERT(updateCount == 1,
   2412                     "SetUpdateCallback should have triggered an update");
   2413  MOZ_RELEASE_ASSERT(IsSameUpdate(update, Update(0, 0, TimeStamp{}, {})));
   2414  updateCount = 0;
   2415  update.Clear();
   2416 
   2417  UniquePtr<ProfileBufferChunk> extantReleasedChunks =
   2418      cm.GetExtantReleasedChunks();
   2419  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Unexpected released chunk(s)");
   2420  MOZ_RELEASE_ASSERT(updateCount == 1,
   2421                     "GetExtantReleasedChunks should have triggered an update");
   2422  MOZ_RELEASE_ASSERT(IsSameUpdate(update, Update(0, 0, TimeStamp{}, {})));
   2423  updateCount = 0;
   2424  update.Clear();
   2425 
   2426  // First request.
   2427  UniquePtr<ProfileBufferChunk> chunk = cm.GetChunk();
   2428  MOZ_RELEASE_ASSERT(!!chunk,
   2429                     "First chunk immediate request should always work");
   2430  const auto chunkActualBufferBytes = chunk->BufferBytes();
   2431  MOZ_RELEASE_ASSERT(updateCount == 1,
   2432                     "GetChunk should have triggered an update");
   2433  MOZ_RELEASE_ASSERT(
   2434      IsSameUpdate(update, Update(chunk->BufferBytes(), 0, TimeStamp{}, {})));
   2435  updateCount = 0;
   2436  update.Clear();
   2437 
   2438  extantReleasedChunks = cm.GetExtantReleasedChunks();
   2439  MOZ_RELEASE_ASSERT(!extantReleasedChunks, "Unexpected released chunk(s)");
   2440  MOZ_RELEASE_ASSERT(updateCount == 1,
   2441                     "GetExtantReleasedChunks should have triggered an update");
   2442  MOZ_RELEASE_ASSERT(
   2443      IsSameUpdate(update, Update(chunk->BufferBytes(), 0, TimeStamp{}, {})));
   2444  updateCount = 0;
   2445  update.Clear();
   2446 
   2447  // For this test, we need to be able to get at least 2 chunks without hitting
   2448  // the limit. (If this failed, it wouldn't necessary be a problem with
   2449  // ProfileBufferChunkManagerWithLocalLimit, fiddle with constants at the top
   2450  // of this test.)
   2451  MOZ_RELEASE_ASSERT(chunkActualBufferBytes < 2 * MaxTotalBytes);
   2452 
   2453  ProfileBufferChunk::Length previousUnreleasedBytes = chunk->BufferBytes();
   2454  ProfileBufferChunk::Length previousReleasedBytes = 0;
   2455  TimeStamp previousOldestDoneTimeStamp;
   2456 
   2457  // We will do enough loops to go through the maximum size a number of times.
   2458  const unsigned Rollovers = 3;
   2459  const unsigned Loops = Rollovers * MaxTotalBytes / chunkActualBufferBytes;
   2460  for (unsigned i = 0; i < Loops; ++i) {
   2461    // Add some data to the chunk.
   2462    const ProfileBufferIndex index =
   2463        ProfileBufferIndex(chunkActualBufferBytes) * i + 1;
   2464    chunk->SetRangeStart(index);
   2465    (void)chunk->ReserveInitialBlockAsTail(1);
   2466    (void)chunk->ReserveBlock(2);
   2467 
   2468    // Request a new chunk.
   2469    UniquePtr<ProfileBufferChunk> newChunk;
   2470    cm.RequestChunk([&](UniquePtr<ProfileBufferChunk> aChunk) {
   2471      newChunk = std::move(aChunk);
   2472    });
   2473    MOZ_RELEASE_ASSERT(updateCount == 0,
   2474                       "RequestChunk() shouldn't have triggered an update");
   2475    cm.FulfillChunkRequests();
   2476    MOZ_RELEASE_ASSERT(!!newChunk, "Chunk request should always work");
   2477    MOZ_RELEASE_ASSERT(newChunk->BufferBytes() == chunkActualBufferBytes,
   2478                       "Unexpected chunk size");
   2479    MOZ_RELEASE_ASSERT(!newChunk->GetNext(), "There should only be one chunk");
   2480 
   2481    MOZ_RELEASE_ASSERT(updateCount == 1,
   2482                       "FulfillChunkRequests() after a request should have "
   2483                       "triggered an update");
   2484    MOZ_RELEASE_ASSERT(!update.IsFinal());
   2485    MOZ_RELEASE_ASSERT(!update.IsNotUpdate());
   2486    MOZ_RELEASE_ASSERT(update.UnreleasedBytes() ==
   2487                       previousUnreleasedBytes + newChunk->BufferBytes());
   2488    previousUnreleasedBytes = update.UnreleasedBytes();
   2489    MOZ_RELEASE_ASSERT(update.ReleasedBytes() <= previousReleasedBytes);
   2490    previousReleasedBytes = update.ReleasedBytes();
   2491    MOZ_RELEASE_ASSERT(previousOldestDoneTimeStamp.IsNull() ||
   2492                       update.OldestDoneTimeStamp() >=
   2493                           previousOldestDoneTimeStamp);
   2494    previousOldestDoneTimeStamp = update.OldestDoneTimeStamp();
   2495    MOZ_RELEASE_ASSERT(update.NewlyReleasedChunksRef().empty());
   2496    updateCount = 0;
   2497    update.Clear();
   2498 
   2499    // Make sure the "Done" timestamp below cannot be the same as from the
   2500    // previous loop.
   2501    const TimeStamp now = TimeStamp::Now();
   2502    while (TimeStamp::Now() == now) {
   2503      ::SleepMilli(1);
   2504    }
   2505 
   2506    // Mark previous chunk done and release it.
   2507    WaitUntilTimeStampChanges();  // Force "done" timestamp to change.
   2508    chunk->MarkDone();
   2509    const auto doneTimeStamp = chunk->ChunkHeader().mDoneTimeStamp;
   2510    const auto bufferBytes = chunk->BufferBytes();
   2511    cm.ReleaseChunk(std::move(chunk));
   2512 
   2513    MOZ_RELEASE_ASSERT(updateCount == 1,
   2514                       "ReleaseChunk() should have triggered an update");
   2515    MOZ_RELEASE_ASSERT(!update.IsFinal());
   2516    MOZ_RELEASE_ASSERT(!update.IsNotUpdate());
   2517    MOZ_RELEASE_ASSERT(update.UnreleasedBytes() ==
   2518                       previousUnreleasedBytes - bufferBytes);
   2519    previousUnreleasedBytes = update.UnreleasedBytes();
   2520    MOZ_RELEASE_ASSERT(update.ReleasedBytes() ==
   2521                       previousReleasedBytes + bufferBytes);
   2522    previousReleasedBytes = update.ReleasedBytes();
   2523    MOZ_RELEASE_ASSERT(previousOldestDoneTimeStamp.IsNull() ||
   2524                       update.OldestDoneTimeStamp() >=
   2525                           previousOldestDoneTimeStamp);
   2526    previousOldestDoneTimeStamp = update.OldestDoneTimeStamp();
   2527    MOZ_RELEASE_ASSERT(update.OldestDoneTimeStamp() <= doneTimeStamp);
   2528    MOZ_RELEASE_ASSERT(update.NewlyReleasedChunksRef().size() == 1);
   2529    MOZ_RELEASE_ASSERT(update.NewlyReleasedChunksRef()[0].mDoneTimeStamp ==
   2530                       doneTimeStamp);
   2531    MOZ_RELEASE_ASSERT(update.NewlyReleasedChunksRef()[0].mBufferBytes ==
   2532                       bufferBytes);
   2533    updateCount = 0;
   2534    update.Clear();
   2535 
   2536    // And cycle to the new chunk.
   2537    chunk = std::move(newChunk);
   2538  }
   2539 
   2540  // Enough testing! Clean-up.
   2541  (void)chunk->ReserveInitialBlockAsTail(0);
   2542  chunk->MarkDone();
   2543  cm.ForgetUnreleasedChunks();
   2544  MOZ_RELEASE_ASSERT(
   2545      updateCount == 1,
   2546      "ForgetUnreleasedChunks() should have triggered an update");
   2547  MOZ_RELEASE_ASSERT(!update.IsFinal());
   2548  MOZ_RELEASE_ASSERT(!update.IsNotUpdate());
   2549  MOZ_RELEASE_ASSERT(update.UnreleasedBytes() == 0);
   2550  MOZ_RELEASE_ASSERT(update.ReleasedBytes() == previousReleasedBytes);
   2551  MOZ_RELEASE_ASSERT(update.NewlyReleasedChunksRef().empty() == 1);
   2552  updateCount = 0;
   2553  update.Clear();
   2554 
   2555  ccm.SetUpdateCallback({});
   2556  MOZ_RELEASE_ASSERT(updateCount == 1,
   2557                     "SetUpdateCallback({}) should have triggered an update");
   2558  MOZ_RELEASE_ASSERT(update.IsFinal());
   2559 
   2560 #  ifdef DEBUG
   2561  cm.DeregisteredFrom(chunkManagerRegisterer);
   2562 #  endif  // DEBUG
   2563 
   2564  printf("TestControlledChunkManagerWithLocalLimit done\n");
   2565 }
   2566 
   2567 #  define VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(                         \
   2568      aProfileChunkedBuffer, aStart, aEnd, aPushed, aCleared, aFailed)        \
   2569    {                                                                         \
   2570      ProfileChunkedBuffer::State state = (aProfileChunkedBuffer).GetState(); \
   2571      MOZ_RELEASE_ASSERT(state.mRangeStart == (aStart));                      \
   2572      MOZ_RELEASE_ASSERT(state.mRangeEnd == (aEnd));                          \
   2573      MOZ_RELEASE_ASSERT(state.mPushedBlockCount == (aPushed));               \
   2574      MOZ_RELEASE_ASSERT(state.mClearedBlockCount == (aCleared));             \
   2575      MOZ_RELEASE_ASSERT(state.mFailedPutBytes == (aFailed));                 \
   2576    }
   2577 
   2578 static void TestChunkedBuffer() {
   2579  printf("TestChunkedBuffer...\n");
   2580 
   2581  ProfileBufferBlockIndex blockIndex;
   2582  MOZ_RELEASE_ASSERT(!blockIndex);
   2583  MOZ_RELEASE_ASSERT(blockIndex == nullptr);
   2584 
   2585  // Create an out-of-session ProfileChunkedBuffer.
   2586  ProfileChunkedBuffer cb(ProfileChunkedBuffer::ThreadSafety::WithMutex);
   2587 
   2588  MOZ_RELEASE_ASSERT(cb.BufferLength().isNothing());
   2589 
   2590  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1, 1, 0, 0, 0);
   2591 
   2592  int result = 0;
   2593  result = cb.ReserveAndPut(
   2594      []() {
   2595        MOZ_RELEASE_ASSERT(false);
   2596        return 1;
   2597      },
   2598      [](Maybe<ProfileBufferEntryWriter>& aEW) { return aEW ? 2 : 3; });
   2599  MOZ_RELEASE_ASSERT(result == 3);
   2600  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1, 1, 0, 0, 0);
   2601 
   2602  result = 0;
   2603  result = cb.Put(
   2604      1, [](Maybe<ProfileBufferEntryWriter>& aEW) { return aEW ? 1 : 2; });
   2605  MOZ_RELEASE_ASSERT(result == 2);
   2606  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1, 1, 0, 0, 0);
   2607 
   2608  blockIndex = cb.PutFrom(&result, 1);
   2609  MOZ_RELEASE_ASSERT(!blockIndex);
   2610  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1, 1, 0, 0, 0);
   2611 
   2612  blockIndex = cb.PutObjects(123, result, "hello");
   2613  MOZ_RELEASE_ASSERT(!blockIndex);
   2614  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1, 1, 0, 0, 0);
   2615 
   2616  blockIndex = cb.PutObject(123);
   2617  MOZ_RELEASE_ASSERT(!blockIndex);
   2618  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1, 1, 0, 0, 0);
   2619 
   2620  auto chunks = cb.GetAllChunks();
   2621  static_assert(std::is_same_v<decltype(chunks), UniquePtr<ProfileBufferChunk>>,
   2622                "ProfileChunkedBuffer::GetAllChunks() should return a "
   2623                "UniquePtr<ProfileBufferChunk>");
   2624  MOZ_RELEASE_ASSERT(!chunks, "Expected no chunks when out-of-session");
   2625 
   2626  bool ran = false;
   2627  result = 0;
   2628  result = cb.Read([&](ProfileChunkedBuffer::Reader* aReader) {
   2629    ran = true;
   2630    MOZ_RELEASE_ASSERT(!aReader);
   2631    return 3;
   2632  });
   2633  MOZ_RELEASE_ASSERT(ran);
   2634  MOZ_RELEASE_ASSERT(result == 3);
   2635 
   2636  cb.ReadEach([](ProfileBufferEntryReader&) { MOZ_RELEASE_ASSERT(false); });
   2637 
   2638  result = 0;
   2639  result = cb.ReadAt(nullptr, [](Maybe<ProfileBufferEntryReader>&& er) {
   2640    MOZ_RELEASE_ASSERT(er.isNothing());
   2641    return 4;
   2642  });
   2643  MOZ_RELEASE_ASSERT(result == 4);
   2644 
   2645  // Use ProfileBufferChunkManagerWithLocalLimit, which will give away
   2646  // ProfileBufferChunks that can contain 128 bytes, using up to 1KB of memory
   2647  // (including usable 128 bytes and headers).
   2648  constexpr size_t bufferMaxSize = 1024;
   2649  constexpr ProfileChunkedBuffer::Length chunkMinSize = 128;
   2650  ProfileBufferChunkManagerWithLocalLimit cm(bufferMaxSize, chunkMinSize);
   2651  cb.SetChunkManager(cm);
   2652  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1, 1, 0, 0, 0);
   2653 
   2654  // Let the chunk manager fulfill the initial request for an extra chunk.
   2655  cm.FulfillChunkRequests();
   2656 
   2657  MOZ_RELEASE_ASSERT(cm.MaxTotalSize() == bufferMaxSize);
   2658  MOZ_RELEASE_ASSERT(cb.BufferLength().isSome());
   2659  MOZ_RELEASE_ASSERT(*cb.BufferLength() == bufferMaxSize);
   2660  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1, 1, 0, 0, 0);
   2661 
   2662  // Write an int with the main `ReserveAndPut` function.
   2663  const int test = 123;
   2664  ran = false;
   2665  blockIndex = nullptr;
   2666  bool success = cb.ReserveAndPut(
   2667      []() { return sizeof(test); },
   2668      [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   2669        ran = true;
   2670        if (!aEW) {
   2671          return false;
   2672        }
   2673        blockIndex = aEW->CurrentBlockIndex();
   2674        MOZ_RELEASE_ASSERT(aEW->RemainingBytes() == sizeof(test));
   2675        aEW->WriteObject(test);
   2676        MOZ_RELEASE_ASSERT(aEW->RemainingBytes() == 0);
   2677        return true;
   2678      });
   2679  MOZ_RELEASE_ASSERT(ran);
   2680  MOZ_RELEASE_ASSERT(success);
   2681  MOZ_RELEASE_ASSERT(blockIndex.ConvertToProfileBufferIndex() == 1);
   2682  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   2683      cb, 1, 1 + ULEB128Size(sizeof(test)) + sizeof(test), 1, 0, 0);
   2684 
   2685  ran = false;
   2686  result = 0;
   2687  result = cb.Read([&](ProfileChunkedBuffer::Reader* aReader) {
   2688    ran = true;
   2689    MOZ_RELEASE_ASSERT(!!aReader);
   2690    // begin() and end() should be at the range edges (verified above).
   2691    MOZ_RELEASE_ASSERT(
   2692        aReader->begin().CurrentBlockIndex().ConvertToProfileBufferIndex() ==
   2693        1);
   2694    MOZ_RELEASE_ASSERT(
   2695        aReader->end().CurrentBlockIndex().ConvertToProfileBufferIndex() == 0);
   2696    // Null ProfileBufferBlockIndex clamped to the beginning.
   2697    MOZ_RELEASE_ASSERT(aReader->At(nullptr) == aReader->begin());
   2698    MOZ_RELEASE_ASSERT(aReader->At(blockIndex) == aReader->begin());
   2699    // At(begin) same as begin().
   2700    MOZ_RELEASE_ASSERT(aReader->At(aReader->begin().CurrentBlockIndex()) ==
   2701                       aReader->begin());
   2702    // At(past block) same as end().
   2703    MOZ_RELEASE_ASSERT(
   2704        aReader->At(ProfileBufferBlockIndex::CreateFromProfileBufferIndex(
   2705            1 + 1 + sizeof(test))) == aReader->end());
   2706 
   2707    size_t read = 0;
   2708    aReader->ForEach([&](ProfileBufferEntryReader& er) {
   2709      ++read;
   2710      MOZ_RELEASE_ASSERT(er.RemainingBytes() == sizeof(test));
   2711      const auto value = er.ReadObject<decltype(test)>();
   2712      MOZ_RELEASE_ASSERT(value == test);
   2713      MOZ_RELEASE_ASSERT(er.RemainingBytes() == 0);
   2714    });
   2715    MOZ_RELEASE_ASSERT(read == 1);
   2716 
   2717    read = 0;
   2718    for (auto er : *aReader) {
   2719      static_assert(std::is_same_v<decltype(er), ProfileBufferEntryReader>,
   2720                    "ProfileChunkedBuffer::Reader range-for should produce "
   2721                    "ProfileBufferEntryReader objects");
   2722      ++read;
   2723      MOZ_RELEASE_ASSERT(er.RemainingBytes() == sizeof(test));
   2724      const auto value = er.ReadObject<decltype(test)>();
   2725      MOZ_RELEASE_ASSERT(value == test);
   2726      MOZ_RELEASE_ASSERT(er.RemainingBytes() == 0);
   2727    };
   2728    MOZ_RELEASE_ASSERT(read == 1);
   2729    return 5;
   2730  });
   2731  MOZ_RELEASE_ASSERT(ran);
   2732  MOZ_RELEASE_ASSERT(result == 5);
   2733 
   2734  // Read the int directly from the ProfileChunkedBuffer, without block index.
   2735  size_t read = 0;
   2736  cb.ReadEach([&](ProfileBufferEntryReader& er) {
   2737    ++read;
   2738    MOZ_RELEASE_ASSERT(er.RemainingBytes() == sizeof(test));
   2739    const auto value = er.ReadObject<decltype(test)>();
   2740    MOZ_RELEASE_ASSERT(value == test);
   2741    MOZ_RELEASE_ASSERT(er.RemainingBytes() == 0);
   2742  });
   2743  MOZ_RELEASE_ASSERT(read == 1);
   2744 
   2745  // Read the int directly from the ProfileChunkedBuffer, with block index.
   2746  read = 0;
   2747  blockIndex = nullptr;
   2748  cb.ReadEach(
   2749      [&](ProfileBufferEntryReader& er, ProfileBufferBlockIndex aBlockIndex) {
   2750        ++read;
   2751        MOZ_RELEASE_ASSERT(!!aBlockIndex);
   2752        MOZ_RELEASE_ASSERT(!blockIndex);
   2753        blockIndex = aBlockIndex;
   2754        MOZ_RELEASE_ASSERT(er.RemainingBytes() == sizeof(test));
   2755        const auto value = er.ReadObject<decltype(test)>();
   2756        MOZ_RELEASE_ASSERT(value == test);
   2757        MOZ_RELEASE_ASSERT(er.RemainingBytes() == 0);
   2758      });
   2759  MOZ_RELEASE_ASSERT(read == 1);
   2760  MOZ_RELEASE_ASSERT(!!blockIndex);
   2761  MOZ_RELEASE_ASSERT(blockIndex != nullptr);
   2762 
   2763  // Read the int from its block index.
   2764  read = 0;
   2765  result = 0;
   2766  result = cb.ReadAt(blockIndex, [&](Maybe<ProfileBufferEntryReader>&& er) {
   2767    ++read;
   2768    MOZ_RELEASE_ASSERT(er.isSome());
   2769    MOZ_RELEASE_ASSERT(er->CurrentBlockIndex() == blockIndex);
   2770    MOZ_RELEASE_ASSERT(!er->NextBlockIndex());
   2771    MOZ_RELEASE_ASSERT(er->RemainingBytes() == sizeof(test));
   2772    const auto value = er->ReadObject<decltype(test)>();
   2773    MOZ_RELEASE_ASSERT(value == test);
   2774    MOZ_RELEASE_ASSERT(er->RemainingBytes() == 0);
   2775    return 6;
   2776  });
   2777  MOZ_RELEASE_ASSERT(result == 6);
   2778  MOZ_RELEASE_ASSERT(read == 1);
   2779 
   2780  MOZ_RELEASE_ASSERT(!cb.IsIndexInCurrentChunk(ProfileBufferIndex{}));
   2781  MOZ_RELEASE_ASSERT(
   2782      cb.IsIndexInCurrentChunk(blockIndex.ConvertToProfileBufferIndex()));
   2783  MOZ_RELEASE_ASSERT(cb.IsIndexInCurrentChunk(cb.GetState().mRangeEnd - 1));
   2784  MOZ_RELEASE_ASSERT(!cb.IsIndexInCurrentChunk(cb.GetState().mRangeEnd));
   2785 
   2786  // No changes after reads.
   2787  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   2788      cb, 1, 1 + ULEB128Size(sizeof(test)) + sizeof(test), 1, 0, 0);
   2789 
   2790  // Steal the underlying ProfileBufferChunks from the ProfileChunkedBuffer.
   2791  chunks = cb.GetAllChunks();
   2792  MOZ_RELEASE_ASSERT(!!chunks, "Expected at least one chunk");
   2793  MOZ_RELEASE_ASSERT(!!chunks->GetNext(), "Expected two chunks");
   2794  MOZ_RELEASE_ASSERT(!chunks->GetNext()->GetNext(), "Expected only two chunks");
   2795  const ProfileChunkedBuffer::Length chunkActualSize = chunks->BufferBytes();
   2796  MOZ_RELEASE_ASSERT(chunkActualSize >= chunkMinSize);
   2797  MOZ_RELEASE_ASSERT(chunks->RangeStart() == 1);
   2798  MOZ_RELEASE_ASSERT(chunks->OffsetFirstBlock() == 0);
   2799  MOZ_RELEASE_ASSERT(chunks->OffsetPastLastBlock() == 1 + sizeof(test));
   2800 
   2801  // GetAllChunks() should have advanced the index one full chunk forward.
   2802  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1 + chunkActualSize,
   2803                                             1 + chunkActualSize, 1, 0, 0);
   2804 
   2805  // Nothing more to read from the now-empty ProfileChunkedBuffer.
   2806  cb.ReadEach([](ProfileBufferEntryReader&) { MOZ_RELEASE_ASSERT(false); });
   2807  cb.ReadEach([](ProfileBufferEntryReader&, ProfileBufferBlockIndex) {
   2808    MOZ_RELEASE_ASSERT(false);
   2809  });
   2810  result = 0;
   2811  result = cb.ReadAt(nullptr, [](Maybe<ProfileBufferEntryReader>&& er) {
   2812    MOZ_RELEASE_ASSERT(er.isNothing());
   2813    return 7;
   2814  });
   2815  MOZ_RELEASE_ASSERT(result == 7);
   2816 
   2817  // Read the int from the stolen chunks.
   2818  read = 0;
   2819  ProfileChunkedBuffer::ReadEach(
   2820      chunks.get(), nullptr,
   2821      [&](ProfileBufferEntryReader& er, ProfileBufferBlockIndex aBlockIndex) {
   2822        ++read;
   2823        MOZ_RELEASE_ASSERT(aBlockIndex == blockIndex);
   2824        MOZ_RELEASE_ASSERT(er.RemainingBytes() == sizeof(test));
   2825        const auto value = er.ReadObject<decltype(test)>();
   2826        MOZ_RELEASE_ASSERT(value == test);
   2827        MOZ_RELEASE_ASSERT(er.RemainingBytes() == 0);
   2828      });
   2829  MOZ_RELEASE_ASSERT(read == 1);
   2830 
   2831  // No changes after reads.
   2832  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, 1 + chunkActualSize,
   2833                                             1 + chunkActualSize, 1, 0, 0);
   2834 
   2835  // Write lots of numbers (by memcpy), which should trigger Chunk destructions.
   2836  ProfileBufferBlockIndex firstBlockIndex;
   2837  MOZ_RELEASE_ASSERT(!firstBlockIndex);
   2838  ProfileBufferBlockIndex lastBlockIndex;
   2839  MOZ_RELEASE_ASSERT(!lastBlockIndex);
   2840  const size_t lots = 2 * bufferMaxSize / (1 + sizeof(int));
   2841  for (size_t i = 1; i < lots; ++i) {
   2842    ProfileBufferBlockIndex blockIndex = cb.PutFrom(&i, sizeof(i));
   2843    MOZ_RELEASE_ASSERT(!!blockIndex);
   2844    MOZ_RELEASE_ASSERT(blockIndex > firstBlockIndex);
   2845    if (!firstBlockIndex) {
   2846      firstBlockIndex = blockIndex;
   2847    }
   2848    MOZ_RELEASE_ASSERT(blockIndex > lastBlockIndex);
   2849    lastBlockIndex = blockIndex;
   2850  }
   2851 
   2852  ProfileChunkedBuffer::State stateAfterPuts = cb.GetState();
   2853  ProfileBufferIndex startAfterPuts = stateAfterPuts.mRangeStart;
   2854  MOZ_RELEASE_ASSERT(startAfterPuts > 1 + chunkActualSize);
   2855  ProfileBufferIndex endAfterPuts = stateAfterPuts.mRangeEnd;
   2856  MOZ_RELEASE_ASSERT(endAfterPuts > startAfterPuts);
   2857  uint64_t pushedAfterPuts = stateAfterPuts.mPushedBlockCount;
   2858  MOZ_RELEASE_ASSERT(pushedAfterPuts > 0);
   2859  uint64_t clearedAfterPuts = stateAfterPuts.mClearedBlockCount;
   2860  MOZ_RELEASE_ASSERT(clearedAfterPuts > 0);
   2861  MOZ_RELEASE_ASSERT(stateAfterPuts.mFailedPutBytes == 0);
   2862  MOZ_RELEASE_ASSERT(!cb.IsIndexInCurrentChunk(ProfileBufferIndex{}));
   2863  MOZ_RELEASE_ASSERT(
   2864      !cb.IsIndexInCurrentChunk(blockIndex.ConvertToProfileBufferIndex()));
   2865  MOZ_RELEASE_ASSERT(
   2866      !cb.IsIndexInCurrentChunk(firstBlockIndex.ConvertToProfileBufferIndex()));
   2867 
   2868  // Read extant numbers, which should at least follow each other.
   2869  read = 0;
   2870  size_t i = 0;
   2871  cb.ReadEach(
   2872      [&](ProfileBufferEntryReader& er, ProfileBufferBlockIndex aBlockIndex) {
   2873        ++read;
   2874        MOZ_RELEASE_ASSERT(!!aBlockIndex);
   2875        MOZ_RELEASE_ASSERT(aBlockIndex > firstBlockIndex);
   2876        MOZ_RELEASE_ASSERT(aBlockIndex <= lastBlockIndex);
   2877        MOZ_RELEASE_ASSERT(er.RemainingBytes() == sizeof(size_t));
   2878        const auto value = er.ReadObject<size_t>();
   2879        if (i == 0) {
   2880          i = value;
   2881        } else {
   2882          MOZ_RELEASE_ASSERT(value == ++i);
   2883        }
   2884        MOZ_RELEASE_ASSERT(er.RemainingBytes() == 0);
   2885      });
   2886  MOZ_RELEASE_ASSERT(read != 0);
   2887  MOZ_RELEASE_ASSERT(read < lots);
   2888 
   2889  // Read first extant number.
   2890  read = 0;
   2891  i = 0;
   2892  blockIndex = nullptr;
   2893  success =
   2894      cb.ReadAt(firstBlockIndex, [&](Maybe<ProfileBufferEntryReader>&& er) {
   2895        MOZ_ASSERT(er.isSome());
   2896        ++read;
   2897        MOZ_RELEASE_ASSERT(er->CurrentBlockIndex() > firstBlockIndex);
   2898        MOZ_RELEASE_ASSERT(!!er->NextBlockIndex());
   2899        MOZ_RELEASE_ASSERT(er->NextBlockIndex() > firstBlockIndex);
   2900        MOZ_RELEASE_ASSERT(er->NextBlockIndex() < lastBlockIndex);
   2901        blockIndex = er->NextBlockIndex();
   2902        MOZ_RELEASE_ASSERT(er->RemainingBytes() == sizeof(size_t));
   2903        const auto value = er->ReadObject<size_t>();
   2904        MOZ_RELEASE_ASSERT(i == 0);
   2905        i = value;
   2906        MOZ_RELEASE_ASSERT(er->RemainingBytes() == 0);
   2907        return 7;
   2908      });
   2909  MOZ_RELEASE_ASSERT(success);
   2910  MOZ_RELEASE_ASSERT(read == 1);
   2911  // Read other extant numbers one by one.
   2912  do {
   2913    bool success =
   2914        cb.ReadAt(blockIndex, [&](Maybe<ProfileBufferEntryReader>&& er) {
   2915          MOZ_ASSERT(er.isSome());
   2916          ++read;
   2917          MOZ_RELEASE_ASSERT(er->CurrentBlockIndex() == blockIndex);
   2918          MOZ_RELEASE_ASSERT(!er->NextBlockIndex() ||
   2919                             er->NextBlockIndex() > blockIndex);
   2920          MOZ_RELEASE_ASSERT(!er->NextBlockIndex() ||
   2921                             er->NextBlockIndex() > firstBlockIndex);
   2922          MOZ_RELEASE_ASSERT(!er->NextBlockIndex() ||
   2923                             er->NextBlockIndex() <= lastBlockIndex);
   2924          MOZ_RELEASE_ASSERT(er->NextBlockIndex()
   2925                                 ? blockIndex < lastBlockIndex
   2926                                 : blockIndex == lastBlockIndex,
   2927                             "er->NextBlockIndex() should only be null when "
   2928                             "blockIndex is at the last block");
   2929          blockIndex = er->NextBlockIndex();
   2930          MOZ_RELEASE_ASSERT(er->RemainingBytes() == sizeof(size_t));
   2931          const auto value = er->ReadObject<size_t>();
   2932          MOZ_RELEASE_ASSERT(value == ++i);
   2933          MOZ_RELEASE_ASSERT(er->RemainingBytes() == 0);
   2934          return true;
   2935        });
   2936    MOZ_RELEASE_ASSERT(success);
   2937  } while (blockIndex);
   2938  MOZ_RELEASE_ASSERT(read > 1);
   2939 
   2940  // No changes after reads.
   2941  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   2942      cb, startAfterPuts, endAfterPuts, pushedAfterPuts, clearedAfterPuts, 0);
   2943 
   2944 #  ifdef DEBUG
   2945  // cb.Dump();
   2946 #  endif
   2947 
   2948  cb.Clear();
   2949 
   2950 #  ifdef DEBUG
   2951  // cb.Dump();
   2952 #  endif
   2953 
   2954  ProfileChunkedBuffer::State stateAfterClear = cb.GetState();
   2955  ProfileBufferIndex startAfterClear = stateAfterClear.mRangeStart;
   2956  MOZ_RELEASE_ASSERT(startAfterClear > startAfterPuts);
   2957  ProfileBufferIndex endAfterClear = stateAfterClear.mRangeEnd;
   2958  MOZ_RELEASE_ASSERT(endAfterClear == startAfterClear);
   2959  MOZ_RELEASE_ASSERT(stateAfterClear.mPushedBlockCount == 0);
   2960  MOZ_RELEASE_ASSERT(stateAfterClear.mClearedBlockCount == 0);
   2961  MOZ_RELEASE_ASSERT(stateAfterClear.mFailedPutBytes == 0);
   2962  MOZ_RELEASE_ASSERT(!cb.IsIndexInCurrentChunk(ProfileBufferIndex{}));
   2963  MOZ_RELEASE_ASSERT(
   2964      !cb.IsIndexInCurrentChunk(blockIndex.ConvertToProfileBufferIndex()));
   2965  MOZ_RELEASE_ASSERT(!cb.IsIndexInCurrentChunk(stateAfterClear.mRangeEnd - 1));
   2966  MOZ_RELEASE_ASSERT(!cb.IsIndexInCurrentChunk(stateAfterClear.mRangeEnd));
   2967 
   2968  // Start writer threads.
   2969  constexpr int ThreadCount = 32;
   2970  std::thread threads[ThreadCount];
   2971  for (int threadNo = 0; threadNo < ThreadCount; ++threadNo) {
   2972    threads[threadNo] = std::thread(
   2973        [&](int aThreadNo) {
   2974          ::SleepMilli(1);
   2975          constexpr int pushCount = 1024;
   2976          for (int push = 0; push < pushCount; ++push) {
   2977            // Reserve as many bytes as the thread number (but at least enough
   2978            // to store an int), and write an increasing int.
   2979            const bool success =
   2980                cb.Put(std::max(aThreadNo, int(sizeof(push))),
   2981                       [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   2982                         if (!aEW) {
   2983                           return false;
   2984                         }
   2985                         aEW->WriteObject(aThreadNo * 1000000 + push);
   2986                         // Advance writer to the end.
   2987                         for (size_t r = aEW->RemainingBytes(); r != 0; --r) {
   2988                           aEW->WriteObject<char>('_');
   2989                         }
   2990                         return true;
   2991                       });
   2992            MOZ_RELEASE_ASSERT(success);
   2993          }
   2994        },
   2995        threadNo);
   2996  }
   2997 
   2998  // Wait for all writer threads to die.
   2999  for (auto&& thread : threads) {
   3000    thread.join();
   3001  }
   3002 
   3003 #  ifdef DEBUG
   3004  // cb.Dump();
   3005 #  endif
   3006 
   3007  ProfileChunkedBuffer::State stateAfterMTPuts = cb.GetState();
   3008  ProfileBufferIndex startAfterMTPuts = stateAfterMTPuts.mRangeStart;
   3009  MOZ_RELEASE_ASSERT(startAfterMTPuts > startAfterClear);
   3010  ProfileBufferIndex endAfterMTPuts = stateAfterMTPuts.mRangeEnd;
   3011  MOZ_RELEASE_ASSERT(endAfterMTPuts > startAfterMTPuts);
   3012  MOZ_RELEASE_ASSERT(stateAfterMTPuts.mPushedBlockCount > 0);
   3013  MOZ_RELEASE_ASSERT(stateAfterMTPuts.mClearedBlockCount > 0);
   3014  MOZ_RELEASE_ASSERT(stateAfterMTPuts.mFailedPutBytes == 0);
   3015 
   3016  // Reset to out-of-session.
   3017  cb.ResetChunkManager();
   3018 
   3019  ProfileChunkedBuffer::State stateAfterReset = cb.GetState();
   3020  ProfileBufferIndex startAfterReset = stateAfterReset.mRangeStart;
   3021  MOZ_RELEASE_ASSERT(startAfterReset == endAfterMTPuts);
   3022  ProfileBufferIndex endAfterReset = stateAfterReset.mRangeEnd;
   3023  MOZ_RELEASE_ASSERT(endAfterReset == startAfterReset);
   3024  MOZ_RELEASE_ASSERT(stateAfterReset.mPushedBlockCount == 0);
   3025  MOZ_RELEASE_ASSERT(stateAfterReset.mClearedBlockCount == 0);
   3026  MOZ_RELEASE_ASSERT(stateAfterReset.mFailedPutBytes == 0);
   3027 
   3028  success = cb.ReserveAndPut(
   3029      []() {
   3030        MOZ_RELEASE_ASSERT(false);
   3031        return 1;
   3032      },
   3033      [](Maybe<ProfileBufferEntryWriter>& aEW) { return !!aEW; });
   3034  MOZ_RELEASE_ASSERT(!success);
   3035  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, startAfterReset, endAfterReset,
   3036                                             0, 0, 0);
   3037 
   3038  success =
   3039      cb.Put(1, [](Maybe<ProfileBufferEntryWriter>& aEW) { return !!aEW; });
   3040  MOZ_RELEASE_ASSERT(!success);
   3041  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, startAfterReset, endAfterReset,
   3042                                             0, 0, 0);
   3043 
   3044  blockIndex = cb.PutFrom(&success, 1);
   3045  MOZ_RELEASE_ASSERT(!blockIndex);
   3046  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, startAfterReset, endAfterReset,
   3047                                             0, 0, 0);
   3048 
   3049  blockIndex = cb.PutObjects(123, success, "hello");
   3050  MOZ_RELEASE_ASSERT(!blockIndex);
   3051  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, startAfterReset, endAfterReset,
   3052                                             0, 0, 0);
   3053 
   3054  blockIndex = cb.PutObject(123);
   3055  MOZ_RELEASE_ASSERT(!blockIndex);
   3056  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, startAfterReset, endAfterReset,
   3057                                             0, 0, 0);
   3058 
   3059  chunks = cb.GetAllChunks();
   3060  MOZ_RELEASE_ASSERT(!chunks, "Expected no chunks when out-of-session");
   3061  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, startAfterReset, endAfterReset,
   3062                                             0, 0, 0);
   3063 
   3064  cb.ReadEach([](ProfileBufferEntryReader&) { MOZ_RELEASE_ASSERT(false); });
   3065  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, startAfterReset, endAfterReset,
   3066                                             0, 0, 0);
   3067 
   3068  success = cb.ReadAt(nullptr, [](Maybe<ProfileBufferEntryReader>&& er) {
   3069    MOZ_RELEASE_ASSERT(er.isNothing());
   3070    return true;
   3071  });
   3072  MOZ_RELEASE_ASSERT(success);
   3073  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cb, startAfterReset, endAfterReset,
   3074                                             0, 0, 0);
   3075 
   3076  printf("TestChunkedBuffer done\n");
   3077 }
   3078 
   3079 static void TestChunkedBufferSingle() {
   3080  printf("TestChunkedBufferSingle...\n");
   3081 
   3082  constexpr ProfileChunkedBuffer::Length chunkMinSize = 128;
   3083 
   3084  // Create a ProfileChunkedBuffer that will own&use a
   3085  // ProfileBufferChunkManagerSingle, which will give away one
   3086  // ProfileBufferChunk that can contain 128 bytes.
   3087  ProfileChunkedBuffer cbSingle(
   3088      ProfileChunkedBuffer::ThreadSafety::WithoutMutex,
   3089      MakeUnique<ProfileBufferChunkManagerSingle>(chunkMinSize));
   3090 
   3091  MOZ_RELEASE_ASSERT(cbSingle.BufferLength().isSome());
   3092  const ProfileChunkedBuffer::Length bufferBytes = *cbSingle.BufferLength();
   3093  MOZ_RELEASE_ASSERT(bufferBytes >= chunkMinSize);
   3094 
   3095  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cbSingle, 1, 1, 0, 0, 0);
   3096 
   3097  // We will write this many blocks to fill the chunk.
   3098  constexpr size_t testBlocks = 4;
   3099  const ProfileChunkedBuffer::Length blockBytes = bufferBytes / testBlocks;
   3100  MOZ_RELEASE_ASSERT(ULEB128Size(blockBytes) == 1,
   3101                     "This test assumes block sizes are small enough so that "
   3102                     "their ULEB128-encoded size is 1 byte");
   3103  const ProfileChunkedBuffer::Length entryBytes =
   3104      blockBytes - ULEB128Size(blockBytes);
   3105 
   3106  // First buffer-filling test: Try to write a too-big entry at the end of the
   3107  // chunk.
   3108 
   3109  // Write all but one block.
   3110  for (size_t i = 0; i < testBlocks - 1; ++i) {
   3111    cbSingle.Put(entryBytes, [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   3112      MOZ_RELEASE_ASSERT(aEW.isSome());
   3113      while (aEW->RemainingBytes() > 0) {
   3114        **aEW = '0' + i;
   3115        ++(*aEW);
   3116      }
   3117    });
   3118    VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   3119        cbSingle, 1, 1 + blockBytes * (i + 1), i + 1, 0, 0);
   3120  }
   3121 
   3122  // Write the last block so that it's too big (by 1 byte) to fit in the chunk,
   3123  // this should fail.
   3124  const ProfileChunkedBuffer::Length remainingBytesForLastBlock =
   3125      bufferBytes - blockBytes * (testBlocks - 1);
   3126  MOZ_RELEASE_ASSERT(ULEB128Size(remainingBytesForLastBlock) == 1,
   3127                     "This test assumes block sizes are small enough so that "
   3128                     "their ULEB128-encoded size is 1 byte");
   3129  const ProfileChunkedBuffer::Length entryToFitRemainingBytes =
   3130      remainingBytesForLastBlock - ULEB128Size(remainingBytesForLastBlock);
   3131  cbSingle.Put(entryToFitRemainingBytes + 1,
   3132               [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   3133                 MOZ_RELEASE_ASSERT(aEW.isNothing());
   3134               });
   3135  // The buffer state should not have changed, apart from the failed bytes.
   3136  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   3137      cbSingle, 1, 1 + blockBytes * (testBlocks - 1), testBlocks - 1, 0,
   3138      remainingBytesForLastBlock + 1);
   3139 
   3140  size_t read = 0;
   3141  cbSingle.ReadEach([&](ProfileBufferEntryReader& aER) {
   3142    MOZ_RELEASE_ASSERT(aER.RemainingBytes() == entryBytes);
   3143    while (aER.RemainingBytes() > 0) {
   3144      MOZ_RELEASE_ASSERT(*aER == '0' + read);
   3145      ++aER;
   3146    }
   3147    ++read;
   3148  });
   3149  MOZ_RELEASE_ASSERT(read == testBlocks - 1);
   3150 
   3151  // ~Interlude~ Test AppendContent:
   3152  // Create another ProfileChunkedBuffer that will use a
   3153  // ProfileBufferChunkManagerWithLocalLimit, which will give away
   3154  // ProfileBufferChunks that can contain 128 bytes, using up to 1KB of memory
   3155  // (including usable 128 bytes and headers).
   3156  constexpr size_t bufferMaxSize = 1024;
   3157  ProfileBufferChunkManagerWithLocalLimit cmTarget(bufferMaxSize, chunkMinSize);
   3158  ProfileChunkedBuffer cbTarget(ProfileChunkedBuffer::ThreadSafety::WithMutex,
   3159                                cmTarget);
   3160 
   3161  // It should start empty.
   3162  cbTarget.ReadEach(
   3163      [](ProfileBufferEntryReader&) { MOZ_RELEASE_ASSERT(false); });
   3164  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cbTarget, 1, 1, 0, 0, 0);
   3165 
   3166  // Copy the contents from cbSingle to cbTarget.
   3167  cbTarget.AppendContents(cbSingle);
   3168 
   3169  // And verify that we now have the same contents in cbTarget.
   3170  read = 0;
   3171  cbTarget.ReadEach([&](ProfileBufferEntryReader& aER) {
   3172    MOZ_RELEASE_ASSERT(aER.RemainingBytes() == entryBytes);
   3173    while (aER.RemainingBytes() > 0) {
   3174      MOZ_RELEASE_ASSERT(*aER == '0' + read);
   3175      ++aER;
   3176    }
   3177    ++read;
   3178  });
   3179  MOZ_RELEASE_ASSERT(read == testBlocks - 1);
   3180  // The state should be the same as the source.
   3181  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   3182      cbTarget, 1, 1 + blockBytes * (testBlocks - 1), testBlocks - 1, 0, 0);
   3183 
   3184 #  ifdef DEBUG
   3185  // cbSingle.Dump();
   3186  // cbTarget.Dump();
   3187 #  endif
   3188 
   3189  // Because we failed to write a too-big chunk above, the chunk was marked
   3190  // full, so that entries should be consistently rejected from now on.
   3191  cbSingle.Put(1, [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   3192    MOZ_RELEASE_ASSERT(aEW.isNothing());
   3193  });
   3194  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   3195      cbSingle, 1, 1 + blockBytes * ((testBlocks - 1)), testBlocks - 1, 0,
   3196      remainingBytesForLastBlock + 1 + ULEB128Size(1u) + 1);
   3197 
   3198  // Clear the buffer before the next test.
   3199 
   3200  cbSingle.Clear();
   3201  // Clear() should move the index to the next chunk range -- even if it's
   3202  // really reusing the same chunk.
   3203  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cbSingle, 1 + bufferBytes,
   3204                                             1 + bufferBytes, 0, 0, 0);
   3205  cbSingle.ReadEach(
   3206      [&](ProfileBufferEntryReader& aER) { MOZ_RELEASE_ASSERT(false); });
   3207 
   3208  // Second buffer-filling test: Try to write a final entry that just fits at
   3209  // the end of the chunk.
   3210 
   3211  // Write all but one block.
   3212  for (size_t i = 0; i < testBlocks - 1; ++i) {
   3213    cbSingle.Put(entryBytes, [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   3214      MOZ_RELEASE_ASSERT(aEW.isSome());
   3215      while (aEW->RemainingBytes() > 0) {
   3216        **aEW = 'a' + i;
   3217        ++(*aEW);
   3218      }
   3219    });
   3220    VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   3221        cbSingle, 1 + bufferBytes, 1 + bufferBytes + blockBytes * (i + 1),
   3222        i + 1, 0, 0);
   3223  }
   3224 
   3225  read = 0;
   3226  cbSingle.ReadEach([&](ProfileBufferEntryReader& aER) {
   3227    MOZ_RELEASE_ASSERT(aER.RemainingBytes() == entryBytes);
   3228    while (aER.RemainingBytes() > 0) {
   3229      MOZ_RELEASE_ASSERT(*aER == 'a' + read);
   3230      ++aER;
   3231    }
   3232    ++read;
   3233  });
   3234  MOZ_RELEASE_ASSERT(read == testBlocks - 1);
   3235 
   3236  // Write the last block so that it fits exactly in the chunk.
   3237  cbSingle.Put(entryToFitRemainingBytes,
   3238               [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   3239                 MOZ_RELEASE_ASSERT(aEW.isSome());
   3240                 while (aEW->RemainingBytes() > 0) {
   3241                   **aEW = 'a' + (testBlocks - 1);
   3242                   ++(*aEW);
   3243                 }
   3244               });
   3245  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   3246      cbSingle, 1 + bufferBytes, 1 + bufferBytes + blockBytes * testBlocks,
   3247      testBlocks, 0, 0);
   3248 
   3249  read = 0;
   3250  cbSingle.ReadEach([&](ProfileBufferEntryReader& aER) {
   3251    MOZ_RELEASE_ASSERT(
   3252        aER.RemainingBytes() ==
   3253        ((read < testBlocks) ? entryBytes : entryToFitRemainingBytes));
   3254    while (aER.RemainingBytes() > 0) {
   3255      MOZ_RELEASE_ASSERT(*aER == 'a' + read);
   3256      ++aER;
   3257    }
   3258    ++read;
   3259  });
   3260  MOZ_RELEASE_ASSERT(read == testBlocks);
   3261 
   3262  // Because the single chunk has been filled, it shouldn't be possible to write
   3263  // more entries.
   3264  cbSingle.Put(1, [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   3265    MOZ_RELEASE_ASSERT(aEW.isNothing());
   3266  });
   3267  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   3268      cbSingle, 1 + bufferBytes, 1 + bufferBytes + blockBytes * testBlocks,
   3269      testBlocks, 0, ULEB128Size(1u) + 1);
   3270 
   3271  cbSingle.Clear();
   3272  // Clear() should move the index to the next chunk range -- even if it's
   3273  // really reusing the same chunk.
   3274  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(cbSingle, 1 + bufferBytes * 2,
   3275                                             1 + bufferBytes * 2, 0, 0, 0);
   3276  cbSingle.ReadEach(
   3277      [&](ProfileBufferEntryReader& aER) { MOZ_RELEASE_ASSERT(false); });
   3278 
   3279  // Clear() recycles the released chunk, so we should be able to record new
   3280  // entries.
   3281  cbSingle.Put(entryBytes, [&](Maybe<ProfileBufferEntryWriter>& aEW) {
   3282    MOZ_RELEASE_ASSERT(aEW.isSome());
   3283    while (aEW->RemainingBytes() > 0) {
   3284      **aEW = 'x';
   3285      ++(*aEW);
   3286    }
   3287  });
   3288  VERIFY_PCB_START_END_PUSHED_CLEARED_FAILED(
   3289      cbSingle, 1 + bufferBytes * 2,
   3290      1 + bufferBytes * 2 + ULEB128Size(entryBytes) + entryBytes, 1, 0, 0);
   3291  read = 0;
   3292  cbSingle.ReadEach([&](ProfileBufferEntryReader& aER) {
   3293    MOZ_RELEASE_ASSERT(read == 0);
   3294    MOZ_RELEASE_ASSERT(aER.RemainingBytes() == entryBytes);
   3295    while (aER.RemainingBytes() > 0) {
   3296      MOZ_RELEASE_ASSERT(*aER == 'x');
   3297      ++aER;
   3298    }
   3299    ++read;
   3300  });
   3301  MOZ_RELEASE_ASSERT(read == 1);
   3302 
   3303  printf("TestChunkedBufferSingle done\n");
   3304 }
   3305 
   3306 static void TestModuloBuffer(ModuloBuffer<>& mb, uint32_t MBSize) {
   3307  using MB = ModuloBuffer<>;
   3308 
   3309  MOZ_RELEASE_ASSERT(mb.BufferLength().Value() == MBSize);
   3310 
   3311  // Iterator comparisons.
   3312  MOZ_RELEASE_ASSERT(mb.ReaderAt(2) == mb.ReaderAt(2));
   3313  MOZ_RELEASE_ASSERT(mb.ReaderAt(2) != mb.ReaderAt(3));
   3314  MOZ_RELEASE_ASSERT(mb.ReaderAt(2) < mb.ReaderAt(3));
   3315  MOZ_RELEASE_ASSERT(mb.ReaderAt(2) <= mb.ReaderAt(2));
   3316  MOZ_RELEASE_ASSERT(mb.ReaderAt(2) <= mb.ReaderAt(3));
   3317  MOZ_RELEASE_ASSERT(mb.ReaderAt(3) > mb.ReaderAt(2));
   3318  MOZ_RELEASE_ASSERT(mb.ReaderAt(2) >= mb.ReaderAt(2));
   3319  MOZ_RELEASE_ASSERT(mb.ReaderAt(3) >= mb.ReaderAt(2));
   3320 
   3321  // Iterators indices don't wrap around (even though they may be pointing at
   3322  // the same location).
   3323  MOZ_RELEASE_ASSERT(mb.ReaderAt(2) != mb.ReaderAt(MBSize + 2));
   3324  MOZ_RELEASE_ASSERT(mb.ReaderAt(MBSize + 2) != mb.ReaderAt(2));
   3325 
   3326  // Dereference.
   3327  static_assert(std::is_same<decltype(*mb.ReaderAt(0)), const MB::Byte&>::value,
   3328                "Dereferencing from a reader should return const Byte*");
   3329  static_assert(std::is_same<decltype(*mb.WriterAt(0)), MB::Byte&>::value,
   3330                "Dereferencing from a writer should return Byte*");
   3331  // Contiguous between 0 and MBSize-1.
   3332  MOZ_RELEASE_ASSERT(&*mb.ReaderAt(MBSize - 1) ==
   3333                     &*mb.ReaderAt(0) + (MBSize - 1));
   3334  // Wraps around.
   3335  MOZ_RELEASE_ASSERT(&*mb.ReaderAt(MBSize) == &*mb.ReaderAt(0));
   3336  MOZ_RELEASE_ASSERT(&*mb.ReaderAt(MBSize + MBSize - 1) ==
   3337                     &*mb.ReaderAt(MBSize - 1));
   3338  MOZ_RELEASE_ASSERT(&*mb.ReaderAt(MBSize + MBSize) == &*mb.ReaderAt(0));
   3339  // Power of 2 modulo wrapping.
   3340  MOZ_RELEASE_ASSERT(&*mb.ReaderAt(uint32_t(-1)) == &*mb.ReaderAt(MBSize - 1));
   3341  MOZ_RELEASE_ASSERT(&*mb.ReaderAt(static_cast<MB::Index>(-1)) ==
   3342                     &*mb.ReaderAt(MBSize - 1));
   3343 
   3344  // Arithmetic.
   3345  MB::Reader arit = mb.ReaderAt(0);
   3346  MOZ_RELEASE_ASSERT(++arit == mb.ReaderAt(1));
   3347  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(1));
   3348 
   3349  MOZ_RELEASE_ASSERT(--arit == mb.ReaderAt(0));
   3350  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(0));
   3351 
   3352  MOZ_RELEASE_ASSERT(arit++ == mb.ReaderAt(0));
   3353  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(1));
   3354 
   3355  MOZ_RELEASE_ASSERT(arit-- == mb.ReaderAt(1));
   3356  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(0));
   3357 
   3358  MOZ_RELEASE_ASSERT(arit + 3 == mb.ReaderAt(3));
   3359  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(0));
   3360 
   3361  MOZ_RELEASE_ASSERT(4 + arit == mb.ReaderAt(4));
   3362  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(0));
   3363 
   3364  // (Can't have assignments inside asserts, hence the split.)
   3365  const bool checkPlusEq = ((arit += 3) == mb.ReaderAt(3));
   3366  MOZ_RELEASE_ASSERT(checkPlusEq);
   3367  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(3));
   3368 
   3369  MOZ_RELEASE_ASSERT((arit - 2) == mb.ReaderAt(1));
   3370  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(3));
   3371 
   3372  const bool checkMinusEq = ((arit -= 2) == mb.ReaderAt(1));
   3373  MOZ_RELEASE_ASSERT(checkMinusEq);
   3374  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(1));
   3375 
   3376  // Random access.
   3377  MOZ_RELEASE_ASSERT(&arit[3] == &*(arit + 3));
   3378  MOZ_RELEASE_ASSERT(arit == mb.ReaderAt(1));
   3379 
   3380  // Iterator difference.
   3381  MOZ_RELEASE_ASSERT(mb.ReaderAt(3) - mb.ReaderAt(1) == 2);
   3382  MOZ_RELEASE_ASSERT(mb.ReaderAt(1) - mb.ReaderAt(3) == MB::Index(-2));
   3383 
   3384  // Only testing Writer, as Reader is just a subset with no code differences.
   3385  MB::Writer it = mb.WriterAt(0);
   3386  MOZ_RELEASE_ASSERT(it.CurrentIndex() == 0);
   3387 
   3388  // Write two characters at the start.
   3389  it.WriteObject('x');
   3390  it.WriteObject('y');
   3391 
   3392  // Backtrack to read them.
   3393  it -= 2;
   3394  // PeekObject should read without moving.
   3395  MOZ_RELEASE_ASSERT(it.PeekObject<char>() == 'x');
   3396  MOZ_RELEASE_ASSERT(it.CurrentIndex() == 0);
   3397  // ReadObject should read and move past the character.
   3398  MOZ_RELEASE_ASSERT(it.ReadObject<char>() == 'x');
   3399  MOZ_RELEASE_ASSERT(it.CurrentIndex() == 1);
   3400  MOZ_RELEASE_ASSERT(it.PeekObject<char>() == 'y');
   3401  MOZ_RELEASE_ASSERT(it.CurrentIndex() == 1);
   3402  MOZ_RELEASE_ASSERT(it.ReadObject<char>() == 'y');
   3403  MOZ_RELEASE_ASSERT(it.CurrentIndex() == 2);
   3404 
   3405  // Checking that a reader can be created from a writer.
   3406  MB::Reader it2(it);
   3407  MOZ_RELEASE_ASSERT(it2.CurrentIndex() == 2);
   3408  // Or assigned.
   3409  it2 = it;
   3410  MOZ_RELEASE_ASSERT(it2.CurrentIndex() == 2);
   3411 
   3412  // Iterator traits.
   3413  static_assert(std::is_same<std::iterator_traits<MB::Reader>::difference_type,
   3414                             MB::Index>::value,
   3415                "ModuloBuffer::Reader::difference_type should be Index");
   3416  static_assert(std::is_same<std::iterator_traits<MB::Reader>::value_type,
   3417                             MB::Byte>::value,
   3418                "ModuloBuffer::Reader::value_type should be Byte");
   3419  static_assert(std::is_same<std::iterator_traits<MB::Reader>::pointer,
   3420                             const MB::Byte*>::value,
   3421                "ModuloBuffer::Reader::pointer should be const Byte*");
   3422  static_assert(std::is_same<std::iterator_traits<MB::Reader>::reference,
   3423                             const MB::Byte&>::value,
   3424                "ModuloBuffer::Reader::reference should be const Byte&");
   3425  static_assert(std::is_base_of<
   3426                    std::input_iterator_tag,
   3427                    std::iterator_traits<MB::Reader>::iterator_category>::value,
   3428                "ModuloBuffer::Reader::iterator_category should be derived "
   3429                "from input_iterator_tag");
   3430  static_assert(std::is_base_of<
   3431                    std::forward_iterator_tag,
   3432                    std::iterator_traits<MB::Reader>::iterator_category>::value,
   3433                "ModuloBuffer::Reader::iterator_category should be derived "
   3434                "from forward_iterator_tag");
   3435  static_assert(std::is_base_of<
   3436                    std::bidirectional_iterator_tag,
   3437                    std::iterator_traits<MB::Reader>::iterator_category>::value,
   3438                "ModuloBuffer::Reader::iterator_category should be derived "
   3439                "from bidirectional_iterator_tag");
   3440  static_assert(
   3441      std::is_same<std::iterator_traits<MB::Reader>::iterator_category,
   3442                   std::random_access_iterator_tag>::value,
   3443      "ModuloBuffer::Reader::iterator_category should be "
   3444      "random_access_iterator_tag");
   3445 
   3446  // Use as input iterator by std::string constructor (which is only considered
   3447  // with proper input iterators.)
   3448  std::string s(mb.ReaderAt(0), mb.ReaderAt(2));
   3449  MOZ_RELEASE_ASSERT(s == "xy");
   3450 
   3451  // Write 4-byte number at index 2.
   3452  it.WriteObject(int32_t(123));
   3453  MOZ_RELEASE_ASSERT(it.CurrentIndex() == 6);
   3454  // And another, which should now wrap around (but index continues on.)
   3455  it.WriteObject(int32_t(456));
   3456  MOZ_RELEASE_ASSERT(it.CurrentIndex() == MBSize + 2);
   3457  // Even though index==MBSize+2, we can read the object we wrote at 2.
   3458  MOZ_RELEASE_ASSERT(it.ReadObject<int32_t>() == 123);
   3459  MOZ_RELEASE_ASSERT(it.CurrentIndex() == MBSize + 6);
   3460  // And similarly, index MBSize+6 points at the same location as index 6.
   3461  MOZ_RELEASE_ASSERT(it.ReadObject<int32_t>() == 456);
   3462  MOZ_RELEASE_ASSERT(it.CurrentIndex() == MBSize + MBSize + 2);
   3463 }
   3464 
   3465 void TestModuloBuffer() {
   3466  printf("TestModuloBuffer...\n");
   3467 
   3468  // Testing ModuloBuffer with default template arguments.
   3469  using MB = ModuloBuffer<>;
   3470 
   3471  // Only 8-byte buffers, to easily test wrap-around.
   3472  constexpr uint32_t MBSize = 8;
   3473 
   3474  // MB with self-allocated heap buffer.
   3475  MB mbByLength(MakePowerOfTwo32<MBSize>());
   3476  TestModuloBuffer(mbByLength, MBSize);
   3477 
   3478  // MB taking ownership of a provided UniquePtr to a buffer.
   3479  auto uniqueBuffer = MakeUnique<uint8_t[]>(MBSize);
   3480  MB mbByUniquePtr(MakeUnique<uint8_t[]>(MBSize), MakePowerOfTwo32<MBSize>());
   3481  TestModuloBuffer(mbByUniquePtr, MBSize);
   3482 
   3483  // MB using part of a buffer on the stack. The buffer is three times the
   3484  // required size: The middle third is where ModuloBuffer will work, the first
   3485  // and last thirds are only used to later verify that ModuloBuffer didn't go
   3486  // out of its bounds.
   3487  uint8_t buffer[MBSize * 3];
   3488  // Pre-fill the buffer with a known pattern, so we can later see what changed.
   3489  for (size_t i = 0; i < MBSize * 3; ++i) {
   3490    buffer[i] = uint8_t('A' + i);
   3491  }
   3492  MB mbByBuffer(&buffer[MBSize], MakePowerOfTwo32<MBSize>());
   3493  TestModuloBuffer(mbByBuffer, MBSize);
   3494 
   3495  // Check that only the provided stack-based sub-buffer was modified.
   3496  uint32_t changed = 0;
   3497  for (size_t i = MBSize; i < MBSize * 2; ++i) {
   3498    changed += (buffer[i] == uint8_t('A' + i)) ? 0 : 1;
   3499  }
   3500  // Expect at least 75% changes.
   3501  MOZ_RELEASE_ASSERT(changed >= MBSize * 6 / 8);
   3502 
   3503  // Everything around the sub-buffer should be unchanged.
   3504  for (size_t i = 0; i < MBSize; ++i) {
   3505    MOZ_RELEASE_ASSERT(buffer[i] == uint8_t('A' + i));
   3506  }
   3507  for (size_t i = MBSize * 2; i < MBSize * 3; ++i) {
   3508    MOZ_RELEASE_ASSERT(buffer[i] == uint8_t('A' + i));
   3509  }
   3510 
   3511  // Check that move-construction is allowed. This verifies that we do not
   3512  // crash from a double free, when `mbByBuffer` and `mbByStolenBuffer` are both
   3513  // destroyed at the end of this function.
   3514  MB mbByStolenBuffer = std::move(mbByBuffer);
   3515  TestModuloBuffer(mbByStolenBuffer, MBSize);
   3516 
   3517  // Check that only the provided stack-based sub-buffer was modified.
   3518  changed = 0;
   3519  for (size_t i = MBSize; i < MBSize * 2; ++i) {
   3520    changed += (buffer[i] == uint8_t('A' + i)) ? 0 : 1;
   3521  }
   3522  // Expect at least 75% changes.
   3523  MOZ_RELEASE_ASSERT(changed >= MBSize * 6 / 8);
   3524 
   3525  // Everything around the sub-buffer should be unchanged.
   3526  for (size_t i = 0; i < MBSize; ++i) {
   3527    MOZ_RELEASE_ASSERT(buffer[i] == uint8_t('A' + i));
   3528  }
   3529  for (size_t i = MBSize * 2; i < MBSize * 3; ++i) {
   3530    MOZ_RELEASE_ASSERT(buffer[i] == uint8_t('A' + i));
   3531  }
   3532 
   3533  // This test function does a `ReadInto` as directed, and checks that the
   3534  // result is the same as if the copy had been done manually byte-by-byte.
   3535  // `TestReadInto(3, 7, 2)` copies from index 3 to index 7, 2 bytes long.
   3536  // Return the output string (from `ReadInto`) for external checks.
   3537  auto TestReadInto = [](MB::Index aReadFrom, MB::Index aWriteTo,
   3538                         MB::Length aBytes) {
   3539    constexpr uint32_t TRISize = 16;
   3540 
   3541    // Prepare an input buffer, all different elements.
   3542    uint8_t input[TRISize + 1] = "ABCDEFGHIJKLMNOP";
   3543    const MB mbInput(input, MakePowerOfTwo32<TRISize>());
   3544 
   3545    // Prepare an output buffer, different from input.
   3546    uint8_t output[TRISize + 1] = "abcdefghijklmnop";
   3547    MB mbOutput(output, MakePowerOfTwo32<TRISize>());
   3548 
   3549    // Run ReadInto.
   3550    auto writer = mbOutput.WriterAt(aWriteTo);
   3551    mbInput.ReaderAt(aReadFrom).ReadInto(writer, aBytes);
   3552 
   3553    // Do the same operation manually.
   3554    uint8_t outputCheck[TRISize + 1] = "abcdefghijklmnop";
   3555    MB mbOutputCheck(outputCheck, MakePowerOfTwo32<TRISize>());
   3556    auto readerCheck = mbInput.ReaderAt(aReadFrom);
   3557    auto writerCheck = mbOutputCheck.WriterAt(aWriteTo);
   3558    for (MB::Length i = 0; i < aBytes; ++i) {
   3559      *writerCheck++ = *readerCheck++;
   3560    }
   3561 
   3562    // Compare the two outputs.
   3563    for (uint32_t i = 0; i < TRISize; ++i) {
   3564 #  ifdef TEST_MODULOBUFFER_FAILURE_DEBUG
   3565      // Only used when debugging failures.
   3566      if (output[i] != outputCheck[i]) {
   3567        printf(
   3568            "*** from=%u to=%u bytes=%u i=%u\ninput:  '%s'\noutput: "
   3569            "'%s'\ncheck:  '%s'\n",
   3570            unsigned(aReadFrom), unsigned(aWriteTo), unsigned(aBytes),
   3571            unsigned(i), input, output, outputCheck);
   3572      }
   3573 #  endif
   3574      MOZ_RELEASE_ASSERT(output[i] == outputCheck[i]);
   3575    }
   3576 
   3577 #  ifdef TEST_MODULOBUFFER_HELPER
   3578    // Only used when adding more tests.
   3579    printf("*** from=%u to=%u bytes=%u output: %s\n", unsigned(aReadFrom),
   3580           unsigned(aWriteTo), unsigned(aBytes), output);
   3581 #  endif
   3582 
   3583    return std::string(reinterpret_cast<const char*>(output));
   3584  };
   3585 
   3586  // A few manual checks:
   3587  constexpr uint32_t TRISize = 16;
   3588  MOZ_RELEASE_ASSERT(TestReadInto(0, 0, 0) == "abcdefghijklmnop");
   3589  MOZ_RELEASE_ASSERT(TestReadInto(0, 0, TRISize) == "ABCDEFGHIJKLMNOP");
   3590  MOZ_RELEASE_ASSERT(TestReadInto(0, 5, TRISize) == "LMNOPABCDEFGHIJK");
   3591  MOZ_RELEASE_ASSERT(TestReadInto(5, 0, TRISize) == "FGHIJKLMNOPABCDE");
   3592 
   3593  // Test everything! (16^3 = 4096, not too much.)
   3594  for (MB::Index r = 0; r < TRISize; ++r) {
   3595    for (MB::Index w = 0; w < TRISize; ++w) {
   3596      for (MB::Length len = 0; len < TRISize; ++len) {
   3597        TestReadInto(r, w, len);
   3598      }
   3599    }
   3600  }
   3601 
   3602  printf("TestModuloBuffer done\n");
   3603 }
   3604 
   3605 void TestLiteralEmptyStringView() {
   3606  printf("TestLiteralEmptyStringView...\n");
   3607 
   3608  static_assert(mozilla::LiteralEmptyStringView<char>() ==
   3609                std::string_view(""));
   3610  static_assert(!!mozilla::LiteralEmptyStringView<char>().data());
   3611  static_assert(mozilla::LiteralEmptyStringView<char>().length() == 0);
   3612 
   3613  static_assert(mozilla::LiteralEmptyStringView<char16_t>() ==
   3614                std::basic_string_view<char16_t>(u""));
   3615  static_assert(!!mozilla::LiteralEmptyStringView<char16_t>().data());
   3616  static_assert(mozilla::LiteralEmptyStringView<char16_t>().length() == 0);
   3617 
   3618  printf("TestLiteralEmptyStringView done\n");
   3619 }
   3620 
   3621 template <typename CHAR>
   3622 void TestProfilerStringView() {
   3623  if constexpr (std::is_same_v<CHAR, char>) {
   3624    printf("TestProfilerStringView<char>...\n");
   3625  } else if constexpr (std::is_same_v<CHAR, char16_t>) {
   3626    printf("TestProfilerStringView<char16_t>...\n");
   3627  } else {
   3628    MOZ_RELEASE_ASSERT(false,
   3629                       "TestProfilerStringView only handles char and char16_t");
   3630  }
   3631 
   3632  // Used to verify implicit constructions, as this will normally be used in
   3633  // function parameters.
   3634  auto BSV = [](mozilla::ProfilerStringView<CHAR>&& aBSV) {
   3635    return std::move(aBSV);
   3636  };
   3637 
   3638  // These look like string literals, as expected by some string constructors.
   3639  const CHAR empty[0 + 1] = {CHAR('\0')};
   3640  const CHAR hi[2 + 1] = {
   3641      CHAR('h'),
   3642      CHAR('i'),
   3643      CHAR('\0'),
   3644  };
   3645 
   3646  // Literal empty string.
   3647  MOZ_RELEASE_ASSERT(BSV(empty).Length() == 0);
   3648  MOZ_RELEASE_ASSERT(BSV(empty).AsSpan().IsEmpty());
   3649  MOZ_RELEASE_ASSERT(BSV(empty).IsLiteral());
   3650  MOZ_RELEASE_ASSERT(!BSV(empty).IsReference());
   3651 
   3652  // Literal non-empty string.
   3653  MOZ_RELEASE_ASSERT(BSV(hi).Length() == 2);
   3654  MOZ_RELEASE_ASSERT(BSV(hi).AsSpan().Elements());
   3655  MOZ_RELEASE_ASSERT(BSV(hi).AsSpan().Elements()[0] == CHAR('h'));
   3656  MOZ_RELEASE_ASSERT(BSV(hi).AsSpan().Elements()[1] == CHAR('i'));
   3657  MOZ_RELEASE_ASSERT(BSV(hi).IsLiteral());
   3658  MOZ_RELEASE_ASSERT(!BSV(hi).IsReference());
   3659 
   3660  // std::string_view to a literal empty string.
   3661  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).Length() == 0);
   3662  MOZ_RELEASE_ASSERT(
   3663      BSV(std::basic_string_view<CHAR>(empty)).AsSpan().IsEmpty());
   3664  MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>(empty)).IsLiteral());
   3665  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(empty)).IsReference());
   3666 
   3667  // std::string_view to a literal non-empty string.
   3668  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).Length() == 2);
   3669  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).AsSpan().Elements());
   3670  MOZ_RELEASE_ASSERT(
   3671      BSV(std::basic_string_view<CHAR>(hi)).AsSpan().Elements()[0] ==
   3672      CHAR('h'));
   3673  MOZ_RELEASE_ASSERT(
   3674      BSV(std::basic_string_view<CHAR>(hi)).AsSpan().Elements()[1] ==
   3675      CHAR('i'));
   3676  MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>(hi)).IsLiteral());
   3677  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>(hi)).IsReference());
   3678 
   3679  // Default std::string_view points at nullptr, ProfilerStringView converts it
   3680  // to the literal empty string.
   3681  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).Length() == 0);
   3682  MOZ_RELEASE_ASSERT(!std::basic_string_view<CHAR>().data());
   3683  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).AsSpan().IsEmpty());
   3684  MOZ_RELEASE_ASSERT(BSV(std::basic_string_view<CHAR>()).IsLiteral());
   3685  MOZ_RELEASE_ASSERT(!BSV(std::basic_string_view<CHAR>()).IsReference());
   3686 
   3687  // std::string to a literal empty string.
   3688  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).Length() == 0);
   3689  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).AsSpan().IsEmpty());
   3690  MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>(empty)).IsLiteral());
   3691  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(empty)).IsReference());
   3692 
   3693  // std::string to a literal non-empty string.
   3694  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).Length() == 2);
   3695  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).AsSpan().Elements());
   3696  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).AsSpan().Elements()[0] ==
   3697                     CHAR('h'));
   3698  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).AsSpan().Elements()[1] ==
   3699                     CHAR('i'));
   3700  MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>(hi)).IsLiteral());
   3701  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>(hi)).IsReference());
   3702 
   3703  // Default std::string contains an empty null-terminated string.
   3704  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).Length() == 0);
   3705  MOZ_RELEASE_ASSERT(std::basic_string<CHAR>().data());
   3706  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).AsSpan().IsEmpty());
   3707  MOZ_RELEASE_ASSERT(!BSV(std::basic_string<CHAR>()).IsLiteral());
   3708  MOZ_RELEASE_ASSERT(BSV(std::basic_string<CHAR>()).IsReference());
   3709 
   3710  // Class that quacks like nsTString (with Data(), Length(), IsLiteral()), to
   3711  // check that ProfilerStringView can read from them.
   3712  class FakeNsTString {
   3713   public:
   3714    FakeNsTString(const CHAR* aData, size_t aLength, bool aIsLiteral)
   3715        : mData(aData), mLength(aLength), mIsLiteral(aIsLiteral) {}
   3716 
   3717    const CHAR* Data() const { return mData; }
   3718    size_t Length() const { return mLength; }
   3719    bool IsLiteral() const { return mIsLiteral; }
   3720 
   3721   private:
   3722    const CHAR* mData;
   3723    size_t mLength;
   3724    bool mIsLiteral;
   3725  };
   3726 
   3727  // FakeNsTString to nullptr.
   3728  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).Length() == 0);
   3729  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).AsSpan().IsEmpty());
   3730  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(nullptr, 0, true)).IsLiteral());
   3731  MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(nullptr, 0, true)).IsReference());
   3732 
   3733  // FakeNsTString to a literal empty string.
   3734  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).Length() == 0);
   3735  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).AsSpan().IsEmpty());
   3736  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(empty, 0, true)).IsLiteral());
   3737  MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(empty, 0, true)).IsReference());
   3738 
   3739  // FakeNsTString to a literal non-empty string.
   3740  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).Length() == 2);
   3741  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).AsSpan().Elements());
   3742  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).AsSpan().Elements()[0] ==
   3743                     CHAR('h'));
   3744  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).AsSpan().Elements()[1] ==
   3745                     CHAR('i'));
   3746  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, true)).IsLiteral());
   3747  MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(hi, 2, true)).IsReference());
   3748 
   3749  // FakeNsTString to a non-literal non-empty string.
   3750  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).Length() == 2);
   3751  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).AsSpan().Elements());
   3752  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).AsSpan().Elements()[0] ==
   3753                     CHAR('h'));
   3754  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).AsSpan().Elements()[1] ==
   3755                     CHAR('i'));
   3756  MOZ_RELEASE_ASSERT(!BSV(FakeNsTString(hi, 2, false)).IsLiteral());
   3757  MOZ_RELEASE_ASSERT(BSV(FakeNsTString(hi, 2, false)).IsReference());
   3758 
   3759  // Serialization and deserialization (with ownership).
   3760  constexpr size_t bufferMaxSize = 1024;
   3761  constexpr ProfileChunkedBuffer::Length chunkMinSize = 128;
   3762  ProfileBufferChunkManagerWithLocalLimit cm(bufferMaxSize, chunkMinSize);
   3763  ProfileChunkedBuffer cb(ProfileChunkedBuffer::ThreadSafety::WithMutex, cm);
   3764 
   3765  // Literal string, serialized as raw pointer.
   3766  MOZ_RELEASE_ASSERT(cb.PutObject(BSV(hi)));
   3767  {
   3768    unsigned read = 0;
   3769    ProfilerStringView<CHAR> outerBSV;
   3770    cb.ReadEach([&](ProfileBufferEntryReader& aER) {
   3771      ++read;
   3772      auto bsv = aER.ReadObject<ProfilerStringView<CHAR>>();
   3773      MOZ_RELEASE_ASSERT(bsv.Length() == 2);
   3774      MOZ_RELEASE_ASSERT(bsv.AsSpan().Elements());
   3775      MOZ_RELEASE_ASSERT(bsv.AsSpan().Elements()[0] == CHAR('h'));
   3776      MOZ_RELEASE_ASSERT(bsv.AsSpan().Elements()[1] == CHAR('i'));
   3777      MOZ_RELEASE_ASSERT(bsv.IsLiteral());
   3778      MOZ_RELEASE_ASSERT(!bsv.IsReference());
   3779      outerBSV = std::move(bsv);
   3780    });
   3781    MOZ_RELEASE_ASSERT(read == 1);
   3782    MOZ_RELEASE_ASSERT(outerBSV.Length() == 2);
   3783    MOZ_RELEASE_ASSERT(outerBSV.AsSpan().Elements());
   3784    MOZ_RELEASE_ASSERT(outerBSV.AsSpan().Elements()[0] == CHAR('h'));
   3785    MOZ_RELEASE_ASSERT(outerBSV.AsSpan().Elements()[1] == CHAR('i'));
   3786    MOZ_RELEASE_ASSERT(outerBSV.IsLiteral());
   3787    MOZ_RELEASE_ASSERT(!outerBSV.IsReference());
   3788  }
   3789 
   3790  MOZ_RELEASE_ASSERT(cb.GetState().mRangeStart == 1u);
   3791 
   3792  cb.Clear();
   3793 
   3794  // Non-literal string, content is serialized.
   3795 
   3796  // We'll try to write 4 strings, such that the 4th one will cross into the
   3797  // next chunk.
   3798  unsigned guessedChunkBytes = unsigned(cb.GetState().mRangeStart) - 1u;
   3799  static constexpr unsigned stringCount = 4u;
   3800  const unsigned stringSize =
   3801      guessedChunkBytes / stringCount / sizeof(CHAR) + 3u;
   3802 
   3803  std::basic_string<CHAR> longString;
   3804  longString.reserve(stringSize);
   3805  for (unsigned i = 0; i < stringSize; ++i) {
   3806    longString += CHAR('0' + i);
   3807  }
   3808 
   3809  for (unsigned i = 0; i < stringCount; ++i) {
   3810    MOZ_RELEASE_ASSERT(cb.PutObject(BSV(longString)));
   3811  }
   3812 
   3813  {
   3814    unsigned read = 0;
   3815    ProfilerStringView<CHAR> outerBSV;
   3816    cb.ReadEach([&](ProfileBufferEntryReader& aER) {
   3817      ++read;
   3818      {
   3819        auto bsv = aER.ReadObject<ProfilerStringView<CHAR>>();
   3820        MOZ_RELEASE_ASSERT(bsv.Length() == stringSize);
   3821        MOZ_RELEASE_ASSERT(bsv.AsSpan().Elements());
   3822        for (unsigned i = 0; i < stringSize; ++i) {
   3823          MOZ_RELEASE_ASSERT(bsv.AsSpan().Elements()[i] == CHAR('0' + i));
   3824          longString += '0' + i;
   3825        }
   3826        MOZ_RELEASE_ASSERT(!bsv.IsLiteral());
   3827        // The first 3 should be references (because they fit in one chunk, so
   3828        // they can be referenced directly), which the 4th one have to be copied
   3829        // out of two chunks and stitched back together.
   3830        MOZ_RELEASE_ASSERT(bsv.IsReference() == (read != 4));
   3831 
   3832        // Test move of ownership.
   3833        outerBSV = std::move(bsv);
   3834        // After a move, references stay complete, while a non-reference had a
   3835        // buffer that has been moved out.
   3836        // NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move)
   3837        MOZ_RELEASE_ASSERT(bsv.Length() == ((read != 4) ? stringSize : 0));
   3838      }
   3839 
   3840      MOZ_RELEASE_ASSERT(outerBSV.Length() == stringSize);
   3841      MOZ_RELEASE_ASSERT(outerBSV.AsSpan().Elements());
   3842      for (unsigned i = 0; i < stringSize; ++i) {
   3843        MOZ_RELEASE_ASSERT(outerBSV.AsSpan().Elements()[i] == CHAR('0' + i));
   3844        longString += '0' + i;
   3845      }
   3846      MOZ_RELEASE_ASSERT(!outerBSV.IsLiteral());
   3847      MOZ_RELEASE_ASSERT(outerBSV.IsReference() == (read != 4));
   3848    });
   3849    MOZ_RELEASE_ASSERT(read == 4);
   3850  }
   3851 
   3852  if constexpr (std::is_same_v<CHAR, char>) {
   3853    printf("TestProfilerStringView<char> done\n");
   3854  } else if constexpr (std::is_same_v<CHAR, char16_t>) {
   3855    printf("TestProfilerStringView<char16_t> done\n");
   3856  }
   3857 }
   3858 
   3859 void TestProfilerDependencies() {
   3860  TestPowerOfTwoMask();
   3861  TestPowerOfTwo();
   3862  TestLEB128();
   3863  TestJSONTimeOutput();
   3864  TestChunk();
   3865  TestChunkManagerSingle();
   3866  TestChunkManagerWithLocalLimit();
   3867  TestControlledChunkManagerUpdate();
   3868  TestControlledChunkManagerWithLocalLimit();
   3869  TestChunkedBuffer();
   3870  TestChunkedBufferSingle();
   3871  TestModuloBuffer();
   3872  TestLiteralEmptyStringView();
   3873  TestProfilerStringView<char>();
   3874  TestProfilerStringView<char16_t>();
   3875 }
   3876 
   3877 // Increase the depth, to a maximum (to avoid too-deep recursion).
   3878 static constexpr size_t NextDepth(size_t aDepth) {
   3879  constexpr size_t MAX_DEPTH = 128;
   3880  return (aDepth < MAX_DEPTH) ? (aDepth + 1) : aDepth;
   3881 }
   3882 
   3883 Atomic<bool, Relaxed> sStopFibonacci;
   3884 
   3885 // Compute fibonacci the hard way (recursively: `f(n)=f(n-1)+f(n-2)`), and
   3886 // prevent inlining.
   3887 // The template parameter makes each depth be a separate function, to better
   3888 // distinguish them in the profiler output.
   3889 template <size_t DEPTH = 0>
   3890 MOZ_NEVER_INLINE unsigned long long Fibonacci(unsigned long long n) {
   3891  AUTO_BASE_PROFILER_LABEL_DYNAMIC_STRING("fib", OTHER, std::to_string(DEPTH));
   3892  if (n == 0) {
   3893    return 0;
   3894  }
   3895  if (n == 1) {
   3896    return 1;
   3897  }
   3898  if (DEPTH < 5 && sStopFibonacci) {
   3899    return 1'000'000'000;
   3900  }
   3901  TimeStamp start = TimeStamp::Now();
   3902  static constexpr size_t MAX_MARKER_DEPTH = 10;
   3903  unsigned long long f2 = Fibonacci<NextDepth(DEPTH)>(n - 2);
   3904  if (DEPTH == 0) {
   3905    BASE_PROFILER_MARKER_UNTYPED("Half-way through Fibonacci", OTHER);
   3906  }
   3907  unsigned long long f1 = Fibonacci<NextDepth(DEPTH)>(n - 1);
   3908  if (DEPTH < MAX_MARKER_DEPTH) {
   3909    BASE_PROFILER_MARKER_TEXT("fib", OTHER,
   3910                              MarkerTiming::IntervalUntilNowFrom(start),
   3911                              std::to_string(DEPTH));
   3912  }
   3913  return f2 + f1;
   3914 }
   3915 
   3916 void TestProfiler() {
   3917  printf("TestProfiler starting -- pid: %" PRIu64 ", tid: %" PRIu64 "\n",
   3918         uint64_t(baseprofiler::profiler_current_process_id().ToNumber()),
   3919         uint64_t(baseprofiler::profiler_current_thread_id().ToNumber()));
   3920  // ::SleepMilli(10000);
   3921 
   3922  TestProfilerDependencies();
   3923 
   3924  {
   3925    MOZ_RELEASE_ASSERT(!baseprofiler::profiler_is_active());
   3926    MOZ_RELEASE_ASSERT(!baseprofiler::profiler_thread_is_being_profiled());
   3927    MOZ_RELEASE_ASSERT(!baseprofiler::profiler_thread_is_sleeping());
   3928 
   3929    const baseprofiler::BaseProfilerThreadId mainThreadId =
   3930        mozilla::baseprofiler::profiler_current_thread_id();
   3931 
   3932    MOZ_RELEASE_ASSERT(mozilla::baseprofiler::profiler_main_thread_id() ==
   3933                       mainThreadId);
   3934    MOZ_RELEASE_ASSERT(mozilla::baseprofiler::profiler_is_main_thread());
   3935 
   3936    std::thread testThread([&]() {
   3937      const baseprofiler::BaseProfilerThreadId testThreadId =
   3938          mozilla::baseprofiler::profiler_current_thread_id();
   3939      MOZ_RELEASE_ASSERT(testThreadId != mainThreadId);
   3940 
   3941      MOZ_RELEASE_ASSERT(mozilla::baseprofiler::profiler_main_thread_id() !=
   3942                         testThreadId);
   3943      MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_is_main_thread());
   3944    });
   3945    testThread.join();
   3946 
   3947    printf("profiler_start()...\n");
   3948    Vector<const char*> filters;
   3949    // Profile all registered threads.
   3950    MOZ_RELEASE_ASSERT(filters.append(""));
   3951    const uint32_t features = baseprofiler::ProfilerFeature::StackWalk;
   3952    baseprofiler::profiler_start(baseprofiler::BASE_PROFILER_DEFAULT_ENTRIES,
   3953                                 BASE_PROFILER_DEFAULT_INTERVAL, features,
   3954                                 filters.begin(), filters.length());
   3955 
   3956    MOZ_RELEASE_ASSERT(baseprofiler::profiler_is_active());
   3957    MOZ_RELEASE_ASSERT(baseprofiler::profiler_thread_is_being_profiled());
   3958    MOZ_RELEASE_ASSERT(!baseprofiler::profiler_thread_is_sleeping());
   3959 
   3960    sStopFibonacci = false;
   3961 
   3962    std::thread threadFib([]() {
   3963      AUTO_BASE_PROFILER_REGISTER_THREAD("fibonacci");
   3964      SleepMilli(5);
   3965      auto cause = baseprofiler::profiler_capture_backtrace();
   3966      AUTO_BASE_PROFILER_MARKER_TEXT(
   3967          "fibonacci", OTHER, MarkerStack::TakeBacktrace(std::move(cause)),
   3968          "First leaf call");
   3969      static const unsigned long long fibStart = 37;
   3970      printf("Fibonacci(%llu)...\n", fibStart);
   3971      AUTO_BASE_PROFILER_LABEL("Label around Fibonacci", OTHER);
   3972 
   3973      unsigned long long f = Fibonacci(fibStart);
   3974      printf("Fibonacci(%llu) = %llu\n", fibStart, f);
   3975    });
   3976 
   3977    std::thread threadCancelFib([]() {
   3978      AUTO_BASE_PROFILER_REGISTER_THREAD("fibonacci canceller");
   3979      SleepMilli(5);
   3980      AUTO_BASE_PROFILER_MARKER_TEXT("fibonacci", OTHER, {}, "Canceller");
   3981      static const int waitMaxSeconds = 10;
   3982      for (int i = 0; i < waitMaxSeconds; ++i) {
   3983        if (sStopFibonacci) {
   3984          AUTO_BASE_PROFILER_LABEL_DYNAMIC_STRING("fibCancel", OTHER,
   3985                                                  std::to_string(i));
   3986          return;
   3987        }
   3988        AUTO_BASE_PROFILER_THREAD_SLEEP;
   3989        SleepMilli(1000);
   3990      }
   3991      AUTO_BASE_PROFILER_LABEL_DYNAMIC_STRING("fibCancel", OTHER,
   3992                                              "Cancelling!");
   3993      sStopFibonacci = true;
   3994    });
   3995 
   3996    {
   3997      AUTO_BASE_PROFILER_MARKER_TEXT("main thread", OTHER, {},
   3998                                     "joining fibonacci thread");
   3999      AUTO_BASE_PROFILER_THREAD_SLEEP;
   4000      threadFib.join();
   4001    }
   4002 
   4003    {
   4004      AUTO_BASE_PROFILER_MARKER_TEXT("main thread", OTHER, {},
   4005                                     "joining fibonacci-canceller thread");
   4006      sStopFibonacci = true;
   4007      AUTO_BASE_PROFILER_THREAD_SLEEP;
   4008      threadCancelFib.join();
   4009    }
   4010 
   4011    // Just making sure all payloads know how to (de)serialize and stream.
   4012 
   4013    MOZ_RELEASE_ASSERT(
   4014        baseprofiler::AddMarker("markers 2.0 without options (omitted)",
   4015                                mozilla::baseprofiler::category::OTHER));
   4016 
   4017    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4018        "markers 2.0 without options (implicit brace-init)",
   4019        mozilla::baseprofiler::category::OTHER, {}));
   4020 
   4021    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4022        "markers 2.0 without options (explicit init)",
   4023        mozilla::baseprofiler::category::OTHER, MarkerOptions()));
   4024 
   4025    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4026        "markers 2.0 without options (explicit brace-init)",
   4027        mozilla::baseprofiler::category::OTHER, MarkerOptions{}));
   4028 
   4029    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4030        "markers 2.0 with one option (implicit)",
   4031        mozilla::baseprofiler::category::OTHER, MarkerInnerWindowId(123)));
   4032 
   4033    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4034        "markers 2.0 with one option (implicit brace-init)",
   4035        mozilla::baseprofiler::category::OTHER, {MarkerInnerWindowId(123)}));
   4036 
   4037    MOZ_RELEASE_ASSERT(
   4038        baseprofiler::AddMarker("markers 2.0 with one option (explicit init)",
   4039                                mozilla::baseprofiler::category::OTHER,
   4040                                MarkerOptions(MarkerInnerWindowId(123))));
   4041 
   4042    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4043        "markers 2.0 with one option (explicit brace-init)",
   4044        mozilla::baseprofiler::category::OTHER,
   4045        MarkerOptions{MarkerInnerWindowId(123)}));
   4046 
   4047    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4048        "markers 2.0 with two options (implicit brace-init)",
   4049        mozilla::baseprofiler::category::OTHER,
   4050        {MarkerInnerWindowId(123), MarkerStack::Capture()}));
   4051 
   4052    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4053        "markers 2.0 with two options (explicit init)",
   4054        mozilla::baseprofiler::category::OTHER,
   4055        MarkerOptions(MarkerInnerWindowId(123), MarkerStack::Capture())));
   4056 
   4057    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4058        "markers 2.0 with two options (explicit brace-init)",
   4059        mozilla::baseprofiler::category::OTHER,
   4060        MarkerOptions{MarkerInnerWindowId(123), MarkerStack::Capture()}));
   4061 
   4062    MOZ_RELEASE_ASSERT(
   4063        baseprofiler::AddMarker("default-templated markers 2.0 without options",
   4064                                mozilla::baseprofiler::category::OTHER));
   4065 
   4066    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4067        "default-templated markers 2.0 with option",
   4068        mozilla::baseprofiler::category::OTHER, MarkerInnerWindowId(123)));
   4069 
   4070    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4071        "explicitly-default-templated markers 2.0 without options",
   4072        mozilla::baseprofiler::category::OTHER, {},
   4073        ::mozilla::baseprofiler::markers::NoPayload{}));
   4074 
   4075    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4076        "explicitly-default-templated markers 2.0 with option",
   4077        mozilla::baseprofiler::category::OTHER, MarkerInnerWindowId(123),
   4078        ::mozilla::baseprofiler::markers::NoPayload{}));
   4079 
   4080    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4081        "stackmarker", mozilla::baseprofiler::category::OTHER, {},
   4082        mozilla::baseprofiler::markers::StackMarker{}));
   4083 
   4084    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4085        "text", mozilla::baseprofiler::category::OTHER, {},
   4086        mozilla::baseprofiler::markers::TextMarker{}, "text text"));
   4087 
   4088    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4089        "media sample", mozilla::baseprofiler::category::OTHER, {},
   4090        mozilla::baseprofiler::markers::MediaSampleMarker{}, 123, 456, 789));
   4091 
   4092    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4093        "video falling behind", mozilla::baseprofiler::category::OTHER, {},
   4094        mozilla::baseprofiler::markers::VideoFallingBehindMarker{}, 123, 456));
   4095 
   4096    MOZ_RELEASE_ASSERT(baseprofiler::AddMarker(
   4097        "video sink render", mozilla::baseprofiler::category::OTHER, {},
   4098        mozilla::baseprofiler::markers::VideoSinkRenderMarker{}, 123));
   4099 
   4100    printf("Sleep 1s...\n");
   4101    {
   4102      AUTO_BASE_PROFILER_THREAD_SLEEP;
   4103      SleepMilli(1000);
   4104    }
   4105 
   4106    printf("baseprofiler_pause()...\n");
   4107    baseprofiler::profiler_pause();
   4108 
   4109    MOZ_RELEASE_ASSERT(!baseprofiler::profiler_thread_is_being_profiled());
   4110 
   4111    Maybe<baseprofiler::ProfilerBufferInfo> info =
   4112        baseprofiler::profiler_get_buffer_info();
   4113    MOZ_RELEASE_ASSERT(info.isSome());
   4114    printf("Profiler buffer range: %llu .. %llu (%llu bytes)\n",
   4115           static_cast<unsigned long long>(info->mRangeStart),
   4116           static_cast<unsigned long long>(info->mRangeEnd),
   4117           // sizeof(ProfileBufferEntry) == 9
   4118           (static_cast<unsigned long long>(info->mRangeEnd) -
   4119            static_cast<unsigned long long>(info->mRangeStart)) *
   4120               9);
   4121    printf("Stats:         min(us) .. mean(us) .. max(us)  [count]\n");
   4122    printf("- Intervals:   %7.1f .. %7.1f  .. %7.1f  [%u]\n",
   4123           info->mIntervalsUs.min,
   4124           info->mIntervalsUs.sum / info->mIntervalsUs.n,
   4125           info->mIntervalsUs.max, info->mIntervalsUs.n);
   4126    printf("- Overheads:   %7.1f .. %7.1f  .. %7.1f  [%u]\n",
   4127           info->mOverheadsUs.min,
   4128           info->mOverheadsUs.sum / info->mOverheadsUs.n,
   4129           info->mOverheadsUs.max, info->mOverheadsUs.n);
   4130    printf("  - Locking:   %7.1f .. %7.1f  .. %7.1f  [%u]\n",
   4131           info->mLockingsUs.min, info->mLockingsUs.sum / info->mLockingsUs.n,
   4132           info->mLockingsUs.max, info->mLockingsUs.n);
   4133    printf("  - Clearning: %7.1f .. %7.1f  .. %7.1f  [%u]\n",
   4134           info->mCleaningsUs.min,
   4135           info->mCleaningsUs.sum / info->mCleaningsUs.n,
   4136           info->mCleaningsUs.max, info->mCleaningsUs.n);
   4137    printf("  - Counters:  %7.1f .. %7.1f  .. %7.1f  [%u]\n",
   4138           info->mCountersUs.min, info->mCountersUs.sum / info->mCountersUs.n,
   4139           info->mCountersUs.max, info->mCountersUs.n);
   4140    printf("  - Threads:   %7.1f .. %7.1f  .. %7.1f  [%u]\n",
   4141           info->mThreadsUs.min, info->mThreadsUs.sum / info->mThreadsUs.n,
   4142           info->mThreadsUs.max, info->mThreadsUs.n);
   4143 
   4144    printf("baseprofiler_get_profile()...\n");
   4145    UniquePtr<char[]> profile = baseprofiler::profiler_get_profile();
   4146 
   4147    // Use a string view over the profile contents, for easier testing.
   4148    std::string_view profileSV = profile.get();
   4149 
   4150    constexpr const auto svnpos = std::string_view::npos;
   4151    // TODO: Properly parse profile and check fields.
   4152    // Check for some expected marker schema JSON output.
   4153    MOZ_RELEASE_ASSERT(profileSV.find("\"markerSchema\":[") != svnpos);
   4154    MOZ_RELEASE_ASSERT(profileSV.find("\"name\":\"Text\",") != svnpos);
   4155    MOZ_RELEASE_ASSERT(profileSV.find("\"name\":\"StackMarker\",") != svnpos);
   4156    MOZ_RELEASE_ASSERT(profileSV.find("\"name\":\"MediaSample\",") != svnpos);
   4157    MOZ_RELEASE_ASSERT(profileSV.find("\"display\":[") != svnpos);
   4158    MOZ_RELEASE_ASSERT(profileSV.find("\"marker-chart\"") != svnpos);
   4159    MOZ_RELEASE_ASSERT(profileSV.find("\"marker-table\"") != svnpos);
   4160    MOZ_RELEASE_ASSERT(profileSV.find("\"format\":\"string\"") != svnpos);
   4161    // TODO: Add more checks for what's expected in the profile. Some of them
   4162    // are done in gtest's.
   4163 
   4164    printf("baseprofiler_save_profile_to_file()...\n");
   4165    baseprofiler::baseprofiler_save_profile_to_file(
   4166        "TestProfiler_profile.json");
   4167 
   4168    printf("profiler_stop()...\n");
   4169    baseprofiler::profiler_stop();
   4170 
   4171    MOZ_RELEASE_ASSERT(!baseprofiler::profiler_is_active());
   4172    MOZ_RELEASE_ASSERT(!baseprofiler::profiler_thread_is_being_profiled());
   4173    MOZ_RELEASE_ASSERT(!baseprofiler::profiler_thread_is_sleeping());
   4174 
   4175    printf("profiler_shutdown()...\n");
   4176  }
   4177 
   4178  printf("TestProfiler done\n");
   4179 }
   4180 
   4181 // Minimal string escaping, similar to how C++ stringliterals should be entered,
   4182 // to help update comparison strings in tests below.
   4183 void printEscaped(std::string_view aString) {
   4184  for (const char c : aString) {
   4185    switch (c) {
   4186      case '\n':
   4187        fprintf(stderr, "\\n\n");
   4188        break;
   4189      case '"':
   4190        fprintf(stderr, "\\\"");
   4191        break;
   4192      case '\\':
   4193        fprintf(stderr, "\\\\");
   4194        break;
   4195      default:
   4196        if (c >= ' ' && c <= '~') {
   4197          fprintf(stderr, "%c", c);
   4198        } else {
   4199          fprintf(stderr, "\\x%02x", unsigned(c));
   4200        }
   4201        break;
   4202    }
   4203  }
   4204 }
   4205 
   4206 // Run aF(SpliceableChunkedJSONWriter&, UniqueJSONStrings&) from inside a JSON
   4207 // array, then output the string table, and compare the full output to
   4208 // aExpected.
   4209 template <typename F>
   4210 static void VerifyUniqueStringContents(
   4211    F&& aF, std::string_view aExpectedData,
   4212    std::string_view aExpectedUniqueStrings,
   4213    mozilla::baseprofiler::UniqueJSONStrings* aUniqueStringsOrNull = nullptr) {
   4214  mozilla::baseprofiler::SpliceableChunkedJSONWriter writer{
   4215      FailureLatchInfallibleSource::Singleton()};
   4216 
   4217  MOZ_RELEASE_ASSERT(!writer.ChunkedWriteFunc().Fallible());
   4218  MOZ_RELEASE_ASSERT(!writer.ChunkedWriteFunc().Failed());
   4219  MOZ_RELEASE_ASSERT(!writer.ChunkedWriteFunc().GetFailure());
   4220  MOZ_RELEASE_ASSERT(&writer.ChunkedWriteFunc().SourceFailureLatch() ==
   4221                     &mozilla::FailureLatchInfallibleSource::Singleton());
   4222  MOZ_RELEASE_ASSERT(
   4223      &std::as_const(writer.ChunkedWriteFunc()).SourceFailureLatch() ==
   4224      &mozilla::FailureLatchInfallibleSource::Singleton());
   4225 
   4226  MOZ_RELEASE_ASSERT(!writer.Fallible());
   4227  MOZ_RELEASE_ASSERT(!writer.Failed());
   4228  MOZ_RELEASE_ASSERT(!writer.GetFailure());
   4229  MOZ_RELEASE_ASSERT(&writer.SourceFailureLatch() ==
   4230                     &mozilla::FailureLatchInfallibleSource::Singleton());
   4231  MOZ_RELEASE_ASSERT(&std::as_const(writer).SourceFailureLatch() ==
   4232                     &mozilla::FailureLatchInfallibleSource::Singleton());
   4233 
   4234  // By default use a local UniqueJSONStrings, otherwise use the one provided.
   4235  mozilla::baseprofiler::UniqueJSONStrings localUniqueStrings{
   4236      FailureLatchInfallibleSource::Singleton()};
   4237  MOZ_RELEASE_ASSERT(!localUniqueStrings.Fallible());
   4238  MOZ_RELEASE_ASSERT(!localUniqueStrings.Failed());
   4239  MOZ_RELEASE_ASSERT(!localUniqueStrings.GetFailure());
   4240  MOZ_RELEASE_ASSERT(&localUniqueStrings.SourceFailureLatch() ==
   4241                     &mozilla::FailureLatchInfallibleSource::Singleton());
   4242  MOZ_RELEASE_ASSERT(&std::as_const(localUniqueStrings).SourceFailureLatch() ==
   4243                     &mozilla::FailureLatchInfallibleSource::Singleton());
   4244 
   4245  mozilla::baseprofiler::UniqueJSONStrings& uniqueStrings =
   4246      aUniqueStringsOrNull ? *aUniqueStringsOrNull : localUniqueStrings;
   4247  MOZ_RELEASE_ASSERT(!uniqueStrings.Failed());
   4248  MOZ_RELEASE_ASSERT(!uniqueStrings.GetFailure());
   4249 
   4250  writer.Start();
   4251  {
   4252    writer.StartArrayProperty("data");
   4253    {
   4254      std::forward<F>(aF)(writer, uniqueStrings);
   4255    }
   4256    writer.EndArray();
   4257 
   4258    writer.StartArrayProperty("stringTable");
   4259    {
   4260      uniqueStrings.SpliceStringTableElements(writer);
   4261    }
   4262    writer.EndArray();
   4263  }
   4264  writer.End();
   4265 
   4266  MOZ_RELEASE_ASSERT(!uniqueStrings.Failed());
   4267  MOZ_RELEASE_ASSERT(!uniqueStrings.GetFailure());
   4268 
   4269  MOZ_RELEASE_ASSERT(!writer.ChunkedWriteFunc().Failed());
   4270  MOZ_RELEASE_ASSERT(!writer.ChunkedWriteFunc().GetFailure());
   4271 
   4272  MOZ_RELEASE_ASSERT(!writer.Failed());
   4273  MOZ_RELEASE_ASSERT(!writer.GetFailure());
   4274 
   4275  UniquePtr<char[]> jsonString = writer.ChunkedWriteFunc().CopyData();
   4276  MOZ_RELEASE_ASSERT(jsonString);
   4277  std::string_view jsonStringView(jsonString.get());
   4278  const size_t length = writer.ChunkedWriteFunc().Length();
   4279  MOZ_RELEASE_ASSERT(length == jsonStringView.length());
   4280  std::string expected = "{\"data\":[";
   4281  expected += aExpectedData;
   4282  expected += "],\"stringTable\":[";
   4283  expected += aExpectedUniqueStrings;
   4284  expected += "]}";
   4285  if (jsonStringView != expected) {
   4286    fprintf(stderr,
   4287            "Expected:\n"
   4288            "------\n");
   4289    printEscaped(expected);
   4290    fprintf(stderr,
   4291            "\n"
   4292            "------\n"
   4293            "Actual:\n"
   4294            "------\n");
   4295    printEscaped(jsonStringView);
   4296    fprintf(stderr,
   4297            "\n"
   4298            "------\n");
   4299  }
   4300  MOZ_RELEASE_ASSERT(jsonStringView == expected);
   4301 }
   4302 
   4303 void TestUniqueJSONStrings() {
   4304  printf("TestUniqueJSONStrings...\n");
   4305 
   4306  using SCJW = mozilla::baseprofiler::SpliceableChunkedJSONWriter;
   4307  using UJS = mozilla::baseprofiler::UniqueJSONStrings;
   4308 
   4309  // Empty everything.
   4310  VerifyUniqueStringContents([](SCJW& aWriter, UJS& aUniqueStrings) {}, "", "");
   4311 
   4312  // Empty unique strings.
   4313  VerifyUniqueStringContents(
   4314      [](SCJW& aWriter, UJS& aUniqueStrings) {
   4315        aWriter.StringElement("string");
   4316      },
   4317      R"("string")", "");
   4318 
   4319  // One unique string.
   4320  VerifyUniqueStringContents(
   4321      [](SCJW& aWriter, UJS& aUniqueStrings) {
   4322        aUniqueStrings.WriteElement(aWriter, "string");
   4323      },
   4324      "0", R"("string")");
   4325 
   4326  // One unique string twice.
   4327  VerifyUniqueStringContents(
   4328      [](SCJW& aWriter, UJS& aUniqueStrings) {
   4329        aUniqueStrings.WriteElement(aWriter, "string");
   4330        aUniqueStrings.WriteElement(aWriter, "string");
   4331      },
   4332      "0,0", R"("string")");
   4333 
   4334  // Two single unique strings.
   4335  VerifyUniqueStringContents(
   4336      [](SCJW& aWriter, UJS& aUniqueStrings) {
   4337        aUniqueStrings.WriteElement(aWriter, "string0");
   4338        aUniqueStrings.WriteElement(aWriter, "string1");
   4339      },
   4340      "0,1", R"("string0","string1")");
   4341 
   4342  // Two unique strings with repetition.
   4343  VerifyUniqueStringContents(
   4344      [](SCJW& aWriter, UJS& aUniqueStrings) {
   4345        aUniqueStrings.WriteElement(aWriter, "string0");
   4346        aUniqueStrings.WriteElement(aWriter, "string1");
   4347        aUniqueStrings.WriteElement(aWriter, "string0");
   4348      },
   4349      "0,1,0", R"("string0","string1")");
   4350 
   4351  // Mix some object properties, for coverage.
   4352  VerifyUniqueStringContents(
   4353      [](SCJW& aWriter, UJS& aUniqueStrings) {
   4354        aUniqueStrings.WriteElement(aWriter, "string0");
   4355        aWriter.StartObjectElement();
   4356        {
   4357          aUniqueStrings.WriteProperty(aWriter, "p0", "prop");
   4358          aUniqueStrings.WriteProperty(aWriter, "p1", "string0");
   4359          aUniqueStrings.WriteProperty(aWriter, "p2", "prop");
   4360        }
   4361        aWriter.EndObject();
   4362        aUniqueStrings.WriteElement(aWriter, "string1");
   4363        aUniqueStrings.WriteElement(aWriter, "string0");
   4364        aUniqueStrings.WriteElement(aWriter, "prop");
   4365      },
   4366      R"(0,{"p0":1,"p1":0,"p2":1},2,0,1)", R"("string0","prop","string1")");
   4367 
   4368  // Unique string table with pre-existing data.
   4369  {
   4370    UJS ujs{FailureLatchInfallibleSource::Singleton()};
   4371    {
   4372      SCJW writer{FailureLatchInfallibleSource::Singleton()};
   4373      ujs.WriteElement(writer, "external0");
   4374      ujs.WriteElement(writer, "external1");
   4375      ujs.WriteElement(writer, "external0");
   4376    }
   4377    VerifyUniqueStringContents(
   4378        [](SCJW& aWriter, UJS& aUniqueStrings) {
   4379          aUniqueStrings.WriteElement(aWriter, "string0");
   4380          aUniqueStrings.WriteElement(aWriter, "string1");
   4381          aUniqueStrings.WriteElement(aWriter, "string0");
   4382        },
   4383        "2,3,2", R"("external0","external1","string0","string1")", &ujs);
   4384  }
   4385 
   4386  // Unique string table with pre-existing data from another table.
   4387  {
   4388    UJS ujs{FailureLatchInfallibleSource::Singleton()};
   4389    {
   4390      SCJW writer{FailureLatchInfallibleSource::Singleton()};
   4391      ujs.WriteElement(writer, "external0");
   4392      ujs.WriteElement(writer, "external1");
   4393      ujs.WriteElement(writer, "external0");
   4394    }
   4395    UJS ujsCopy(FailureLatchInfallibleSource::Singleton(), ujs,
   4396                mozilla::ProgressLogger{});
   4397    VerifyUniqueStringContents(
   4398        [](SCJW& aWriter, UJS& aUniqueStrings) {
   4399          aUniqueStrings.WriteElement(aWriter, "string0");
   4400          aUniqueStrings.WriteElement(aWriter, "string1");
   4401          aUniqueStrings.WriteElement(aWriter, "string0");
   4402        },
   4403        "2,3,2", R"("external0","external1","string0","string1")", &ujs);
   4404  }
   4405 
   4406  // Unique string table through SpliceableJSONWriter.
   4407  VerifyUniqueStringContents(
   4408      [](SCJW& aWriter, UJS& aUniqueStrings) {
   4409        aWriter.SetUniqueStrings(aUniqueStrings);
   4410        aWriter.UniqueStringElement("string0");
   4411        aWriter.StartObjectElement();
   4412        {
   4413          aWriter.UniqueStringProperty("p0", "prop");
   4414          aWriter.UniqueStringProperty("p1", "string0");
   4415          aWriter.UniqueStringProperty("p2", "prop");
   4416        }
   4417        aWriter.EndObject();
   4418        aWriter.UniqueStringElement("string1");
   4419        aWriter.UniqueStringElement("string0");
   4420        aWriter.UniqueStringElement("prop");
   4421        aWriter.ResetUniqueStrings();
   4422      },
   4423      R"(0,{"p0":1,"p1":0,"p2":1},2,0,1)", R"("string0","prop","string1")");
   4424 
   4425  printf("TestUniqueJSONStrings done\n");
   4426 }
   4427 
   4428 void StreamMarkers(const mozilla::ProfileChunkedBuffer& aBuffer,
   4429                   mozilla::baseprofiler::SpliceableJSONWriter& aWriter) {
   4430  aWriter.StartArrayProperty("data");
   4431  {
   4432    aBuffer.ReadEach([&](mozilla::ProfileBufferEntryReader& aEntryReader) {
   4433      mozilla::ProfileBufferEntryKind entryKind =
   4434          aEntryReader.ReadObject<mozilla::ProfileBufferEntryKind>();
   4435      MOZ_RELEASE_ASSERT(entryKind == mozilla::ProfileBufferEntryKind::Marker);
   4436 
   4437      mozilla::base_profiler_markers_detail::DeserializeAfterKindAndStream(
   4438          aEntryReader,
   4439          [&](const mozilla::baseprofiler::BaseProfilerThreadId&) {
   4440            return &aWriter;
   4441          },
   4442          [&](mozilla::ProfileChunkedBuffer&) {
   4443            aWriter.StringElement("Real backtrace would be here");
   4444          },
   4445          [&](mozilla::base_profiler_markers_detail::Streaming::
   4446                  DeserializerTag) {});
   4447    });
   4448  }
   4449  aWriter.EndArray();
   4450 }
   4451 
   4452 void PrintMarkers(const mozilla::ProfileChunkedBuffer& aBuffer) {
   4453  mozilla::baseprofiler::SpliceableJSONWriter writer(
   4454      mozilla::MakeUnique<mozilla::baseprofiler::OStreamJSONWriteFunc>(
   4455          std::cout),
   4456      FailureLatchInfallibleSource::Singleton());
   4457  mozilla::baseprofiler::UniqueJSONStrings uniqueStrings{
   4458      FailureLatchInfallibleSource::Singleton()};
   4459  writer.SetUniqueStrings(uniqueStrings);
   4460  writer.Start();
   4461  {
   4462    StreamMarkers(aBuffer, writer);
   4463 
   4464    writer.StartArrayProperty("stringTable");
   4465    {
   4466      uniqueStrings.SpliceStringTableElements(writer);
   4467    }
   4468    writer.EndArray();
   4469  }
   4470  writer.End();
   4471  writer.ResetUniqueStrings();
   4472 }
   4473 
   4474 static void SubTestMarkerCategory(
   4475    const mozilla::MarkerCategory& aMarkerCategory,
   4476    const mozilla::baseprofiler::ProfilingCategoryPair& aProfilingCategoryPair,
   4477    const mozilla::baseprofiler::ProfilingCategory& aProfilingCategory) {
   4478  MOZ_RELEASE_ASSERT(aMarkerCategory.CategoryPair() == aProfilingCategoryPair,
   4479                     "Unexpected MarkerCategory::CategoryPair()");
   4480 
   4481  MOZ_RELEASE_ASSERT(
   4482      mozilla::MarkerCategory(aProfilingCategoryPair).CategoryPair() ==
   4483          aProfilingCategoryPair,
   4484      "MarkerCategory(<name>).CategoryPair() should return <name>");
   4485 
   4486  MOZ_RELEASE_ASSERT(aMarkerCategory.GetCategory() == aProfilingCategory,
   4487                     "Unexpected MarkerCategory::GetCategory()");
   4488 
   4489  mozilla::ProfileBufferChunkManagerSingle chunkManager(512);
   4490  mozilla::ProfileChunkedBuffer buffer(
   4491      mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
   4492  mozilla::ProfileBufferBlockIndex i = buffer.PutObject(aMarkerCategory);
   4493  MOZ_RELEASE_ASSERT(i != mozilla::ProfileBufferBlockIndex{},
   4494                     "Failed serialization");
   4495  buffer.ReadEach([&](mozilla::ProfileBufferEntryReader& aER,
   4496                      mozilla::ProfileBufferBlockIndex aIndex) {
   4497    MOZ_RELEASE_ASSERT(aIndex == i, "Unexpected deserialization index");
   4498    const auto readCategory = aER.ReadObject<mozilla::MarkerCategory>();
   4499    MOZ_RELEASE_ASSERT(aER.RemainingBytes() == 0,
   4500                       "Unexpected extra serialized bytes");
   4501    MOZ_RELEASE_ASSERT(readCategory.CategoryPair() == aProfilingCategoryPair,
   4502                       "Incorrect deserialization value");
   4503  });
   4504 }
   4505 
   4506 void TestMarkerCategory() {
   4507  printf("TestMarkerCategory...\n");
   4508 
   4509  mozilla::ProfileBufferChunkManagerSingle chunkManager(512);
   4510  mozilla::ProfileChunkedBuffer buffer(
   4511      mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
   4512 
   4513 #  define CATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color)
   4514 #  define CATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString)     \
   4515    static_assert(                                                          \
   4516        std::is_same_v<decltype(mozilla::baseprofiler::category::name),     \
   4517                       const mozilla::MarkerCategory>,                      \
   4518        "baseprofiler::category::<name> should be a const MarkerCategory"); \
   4519                                                                            \
   4520    SubTestMarkerCategory(                                                  \
   4521        mozilla::baseprofiler::category::name,                              \
   4522        mozilla::baseprofiler::ProfilingCategoryPair::name,                 \
   4523        mozilla::baseprofiler::ProfilingCategory::supercategory);
   4524 #  define CATEGORY_ENUM_END_CATEGORY
   4525  MOZ_PROFILING_CATEGORY_LIST(CATEGORY_ENUM_BEGIN_CATEGORY,
   4526                              CATEGORY_ENUM_SUBCATEGORY,
   4527                              CATEGORY_ENUM_END_CATEGORY)
   4528 #  undef CATEGORY_ENUM_BEGIN_CATEGORY
   4529 #  undef CATEGORY_ENUM_SUBCATEGORY
   4530 #  undef CATEGORY_ENUM_END_CATEGORY
   4531 
   4532  printf("TestMarkerCategory done\n");
   4533 }
   4534 
   4535 void TestMarkerThreadId() {
   4536  printf("TestMarkerThreadId...\n");
   4537 
   4538  MOZ_RELEASE_ASSERT(MarkerThreadId{}.IsUnspecified());
   4539  MOZ_RELEASE_ASSERT(!MarkerThreadId::MainThread().IsUnspecified());
   4540  MOZ_RELEASE_ASSERT(!MarkerThreadId::CurrentThread().IsUnspecified());
   4541 
   4542  MOZ_RELEASE_ASSERT(!MarkerThreadId{
   4543      mozilla::baseprofiler::BaseProfilerThreadId::FromNumber(42)}
   4544                          .IsUnspecified());
   4545  MOZ_RELEASE_ASSERT(
   4546      MarkerThreadId{
   4547          mozilla::baseprofiler::BaseProfilerThreadId::FromNumber(42)}
   4548          .ThreadId()
   4549          .ToNumber() == 42);
   4550 
   4551  // We'll assume that this test runs in the main thread (which should be true
   4552  // when called from the `main` function).
   4553  MOZ_RELEASE_ASSERT(MarkerThreadId::MainThread().ThreadId() ==
   4554                     mozilla::baseprofiler::profiler_main_thread_id());
   4555 
   4556  MOZ_RELEASE_ASSERT(MarkerThreadId::CurrentThread().ThreadId() ==
   4557                     mozilla::baseprofiler::profiler_current_thread_id());
   4558 
   4559  MOZ_RELEASE_ASSERT(MarkerThreadId::CurrentThread().ThreadId() ==
   4560                     mozilla::baseprofiler::profiler_main_thread_id());
   4561 
   4562  std::thread testThread([]() {
   4563    MOZ_RELEASE_ASSERT(!MarkerThreadId::MainThread().IsUnspecified());
   4564    MOZ_RELEASE_ASSERT(!MarkerThreadId::CurrentThread().IsUnspecified());
   4565 
   4566    MOZ_RELEASE_ASSERT(MarkerThreadId::MainThread().ThreadId() ==
   4567                       mozilla::baseprofiler::profiler_main_thread_id());
   4568 
   4569    MOZ_RELEASE_ASSERT(MarkerThreadId::CurrentThread().ThreadId() ==
   4570                       mozilla::baseprofiler::profiler_current_thread_id());
   4571 
   4572    MOZ_RELEASE_ASSERT(MarkerThreadId::CurrentThread().ThreadId() !=
   4573                       mozilla::baseprofiler::profiler_main_thread_id());
   4574  });
   4575  testThread.join();
   4576 
   4577  printf("TestMarkerThreadId done\n");
   4578 }
   4579 
   4580 void TestMarkerNoPayload() {
   4581  printf("TestMarkerNoPayload...\n");
   4582 
   4583  mozilla::ProfileBufferChunkManagerSingle chunkManager(512);
   4584  mozilla::ProfileChunkedBuffer buffer(
   4585      mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
   4586 
   4587  mozilla::ProfileBufferBlockIndex i0 =
   4588      mozilla::baseprofiler::AddMarkerToBuffer(
   4589          buffer, "literal", mozilla::baseprofiler::category::OTHER_Profiling);
   4590  MOZ_RELEASE_ASSERT(i0);
   4591 
   4592  const std::string dynamic = "dynamic";
   4593  mozilla::ProfileBufferBlockIndex i1 =
   4594      mozilla::baseprofiler::AddMarkerToBuffer(
   4595          buffer, dynamic,
   4596          mozilla::baseprofiler::category::GRAPHICS_FlushingAsyncPaints, {});
   4597  MOZ_RELEASE_ASSERT(i1);
   4598  MOZ_RELEASE_ASSERT(i1 > i0);
   4599 
   4600  mozilla::ProfileBufferBlockIndex i2 =
   4601      mozilla::baseprofiler::AddMarkerToBuffer(
   4602          buffer, std::string_view("string_view"),
   4603          mozilla::baseprofiler::category::GRAPHICS_FlushingAsyncPaints, {});
   4604  MOZ_RELEASE_ASSERT(i2);
   4605  MOZ_RELEASE_ASSERT(i2 > i1);
   4606 
   4607 #  ifdef DEBUG
   4608  buffer.Dump();
   4609 #  endif
   4610 
   4611  PrintMarkers(buffer);
   4612 
   4613  printf("TestMarkerNoPayload done\n");
   4614 }
   4615 
   4616 void TestUserMarker() {
   4617  printf("TestUserMarker...\n");
   4618 
   4619  // User-defined marker type with text.
   4620  // It's fine to define it right in the function where it's used.
   4621  struct MarkerTypeTestMinimal {
   4622    static constexpr Span<const char> MarkerTypeName() {
   4623      return MakeStringSpan("test-minimal");
   4624    }
   4625    static void StreamJSONMarkerData(
   4626        mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
   4627        const std::string& aText) {
   4628      aWriter.StringProperty("text", aText);
   4629    }
   4630    static mozilla::MarkerSchema MarkerTypeDisplay() {
   4631      using MS = mozilla::MarkerSchema;
   4632      MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
   4633      schema.SetTooltipLabel("tooltip for test-minimal");
   4634      schema.AddKeyLabelFormat("text", "Text", MS::Format::String,
   4635                               MS::PayloadFlags::Searchable);
   4636      return schema;
   4637    }
   4638  };
   4639 
   4640  mozilla::ProfileBufferChunkManagerSingle chunkManager(1024);
   4641  mozilla::ProfileChunkedBuffer buffer(
   4642      mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
   4643 
   4644  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4645      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling, {},
   4646      MarkerTypeTestMinimal{}, std::string("payload text")));
   4647 
   4648  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4649      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
   4650      mozilla::MarkerThreadId(
   4651          mozilla::baseprofiler::BaseProfilerThreadId::FromNumber(123)),
   4652      MarkerTypeTestMinimal{}, std::string("ThreadId(123)")));
   4653 
   4654  auto start = mozilla::TimeStamp::Now();
   4655 
   4656  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4657      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
   4658      mozilla::MarkerTiming::InstantAt(start), MarkerTypeTestMinimal{},
   4659      std::string("InstantAt(start)")));
   4660 
   4661  auto then = mozilla::TimeStamp::Now();
   4662 
   4663  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4664      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
   4665      mozilla::MarkerTiming::IntervalStart(start), MarkerTypeTestMinimal{},
   4666      std::string("IntervalStart(start)")));
   4667 
   4668  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4669      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
   4670      mozilla::MarkerTiming::IntervalEnd(then), MarkerTypeTestMinimal{},
   4671      std::string("IntervalEnd(then)")));
   4672 
   4673  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4674      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
   4675      mozilla::MarkerTiming::Interval(start, then), MarkerTypeTestMinimal{},
   4676      std::string("Interval(start, then)")));
   4677 
   4678  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4679      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
   4680      mozilla::MarkerTiming::IntervalUntilNowFrom(start),
   4681      MarkerTypeTestMinimal{}, std::string("IntervalUntilNowFrom(start)")));
   4682 
   4683  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4684      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
   4685      mozilla::MarkerStack::NoStack(), MarkerTypeTestMinimal{},
   4686      std::string("NoStack")));
   4687  // Note: We cannot test stack-capture here, because the profiler is not
   4688  // initialized.
   4689 
   4690  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4691      buffer, "test2", mozilla::baseprofiler::category::OTHER_Profiling,
   4692      mozilla::MarkerInnerWindowId(123), MarkerTypeTestMinimal{},
   4693      std::string("InnerWindowId(123)")));
   4694 
   4695 #  ifdef DEBUG
   4696  buffer.Dump();
   4697 #  endif
   4698 
   4699  PrintMarkers(buffer);
   4700 
   4701  printf("TestUserMarker done\n");
   4702 }
   4703 
   4704 void TestPredefinedMarkers() {
   4705  printf("TestPredefinedMarkers...\n");
   4706 
   4707  mozilla::ProfileBufferChunkManagerSingle chunkManager(1024);
   4708  mozilla::ProfileChunkedBuffer buffer(
   4709      mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager);
   4710 
   4711  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4712      buffer, std::string_view("stackmarker"),
   4713      mozilla::baseprofiler::category::OTHER, {},
   4714      mozilla::baseprofiler::markers::StackMarker{}));
   4715 
   4716  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4717      buffer, std::string_view("text"), mozilla::baseprofiler::category::OTHER,
   4718      {}, mozilla::baseprofiler::markers::TextMarker{}, "text text"));
   4719 
   4720  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4721      buffer, std::string_view("media"), mozilla::baseprofiler::category::OTHER,
   4722      {}, mozilla::baseprofiler::markers::MediaSampleMarker{}, 123, 456, 789));
   4723 
   4724  MOZ_RELEASE_ASSERT(mozilla::baseprofiler::AddMarkerToBuffer(
   4725      buffer, std::string_view("media"), mozilla::baseprofiler::category::OTHER,
   4726      {}, mozilla::baseprofiler::markers::VideoFallingBehindMarker{}, 123,
   4727      456));
   4728 
   4729 #  ifdef DEBUG
   4730  buffer.Dump();
   4731 #  endif
   4732 
   4733  PrintMarkers(buffer);
   4734 
   4735  printf("TestPredefinedMarkers done\n");
   4736 }
   4737 
   4738 void TestProfilerMarkers() {
   4739  printf(
   4740      "TestProfilerMarkers -- pid: %" PRIu64 ", tid: %" PRIu64 "\n",
   4741      uint64_t(mozilla::baseprofiler::profiler_current_process_id().ToNumber()),
   4742      uint64_t(mozilla::baseprofiler::profiler_current_thread_id().ToNumber()));
   4743  // ::SleepMilli(10000);
   4744 
   4745  TestUniqueJSONStrings();
   4746  TestMarkerCategory();
   4747  TestMarkerThreadId();
   4748  TestMarkerNoPayload();
   4749  TestUserMarker();
   4750  TestPredefinedMarkers();
   4751 
   4752  printf("TestProfilerMarkers done\n");
   4753 }
   4754 
   4755 #else  // MOZ_GECKO_PROFILER
   4756 
   4757 // Testing that macros are still #defined (but do nothing) when
   4758 // MOZ_GECKO_PROFILER is disabled.
   4759 void TestProfiler() {
   4760  // These don't need to make sense, we just want to know that they're defined
   4761  // and don't do anything.
   4762 
   4763 #  ifndef AUTO_BASE_PROFILER_INIT
   4764 #    error AUTO_BASE_PROFILER_INIT not #defined
   4765 #  endif  // AUTO_BASE_PROFILER_INIT
   4766  AUTO_BASE_PROFILER_INIT;
   4767 
   4768 #  ifndef AUTO_BASE_PROFILER_MARKER_TEXT
   4769 #    error AUTO_BASE_PROFILER_MARKER_TEXT not #defined
   4770 #  endif  // AUTO_BASE_PROFILER_MARKER_TEXT
   4771 
   4772 #  ifndef AUTO_BASE_PROFILER_LABEL
   4773 #    error AUTO_BASE_PROFILER_LABEL not #defined
   4774 #  endif  // AUTO_BASE_PROFILER_LABEL
   4775 
   4776 #  ifndef AUTO_BASE_PROFILER_THREAD_SLEEP
   4777 #    error AUTO_BASE_PROFILER_THREAD_SLEEP not #defined
   4778 #  endif  // AUTO_BASE_PROFILER_THREAD_SLEEP
   4779  AUTO_BASE_PROFILER_THREAD_SLEEP;
   4780 
   4781 #  ifndef BASE_PROFILER_MARKER_UNTYPED
   4782 #    error BASE_PROFILER_MARKER_UNTYPED not #defined
   4783 #  endif  // BASE_PROFILER_MARKER_UNTYPED
   4784 
   4785 #  ifndef BASE_PROFILER_MARKER
   4786 #    error BASE_PROFILER_MARKER not #defined
   4787 #  endif  // BASE_PROFILER_MARKER
   4788 
   4789 #  ifndef BASE_PROFILER_MARKER_TEXT
   4790 #    error BASE_PROFILER_MARKER_TEXT not #defined
   4791 #  endif  // BASE_PROFILER_MARKER_TEXT
   4792 
   4793  MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_get_backtrace(),
   4794                     "profiler_get_backtrace should return nullptr");
   4795  mozilla::ProfileChunkedBuffer buffer(
   4796      mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex);
   4797  MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_capture_backtrace_into(
   4798                         buffer, mozilla::StackCaptureOptions::Full),
   4799                     "profiler_capture_backtrace_into should return false");
   4800  MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_capture_backtrace(),
   4801                     "profiler_capture_backtrace should return nullptr");
   4802 }
   4803 
   4804 // Testing that macros are still #defined (but do nothing) when
   4805 // MOZ_GECKO_PROFILER is disabled.
   4806 void TestProfilerMarkers() {
   4807  // These don't need to make sense, we just want to know that they're defined
   4808  // and don't do anything.
   4809 }
   4810 
   4811 #endif  // MOZ_GECKO_PROFILER else
   4812 
   4813 #if defined(XP_WIN)
   4814 int wmain()
   4815 #else
   4816 int main()
   4817 #endif  // defined(XP_WIN)
   4818 {
   4819 #ifdef MOZ_GECKO_PROFILER
   4820  printf("BaseTestProfiler -- pid: %" PRIu64 ", tid: %" PRIu64 "\n",
   4821         uint64_t(baseprofiler::profiler_current_process_id().ToNumber()),
   4822         uint64_t(baseprofiler::profiler_current_thread_id().ToNumber()));
   4823  // ::SleepMilli(10000);
   4824 #endif  // MOZ_GECKO_PROFILER
   4825 
   4826  TestFailureLatch();
   4827  TestProfilerUtils();
   4828  TestBaseAndProfilerDetail();
   4829  TestSharedMutex();
   4830  TestProportionValue();
   4831  TestProgressLogger();
   4832  // Note that there are two `TestProfiler{,Markers}` functions above, depending
   4833  // on whether MOZ_GECKO_PROFILER is #defined.
   4834  {
   4835    printf("profiler_init()...\n");
   4836    AUTO_BASE_PROFILER_INIT;
   4837 
   4838    TestProfiler();
   4839    TestProfilerMarkers();
   4840  }
   4841 
   4842  return 0;
   4843 }