tor-browser

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

ProfileBufferEntry.h (10877B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef ProfileBufferEntry_h
      8 #define ProfileBufferEntry_h
      9 
     10 #include "gtest/MozGtestFriend.h"
     11 #include "mozilla/BaseProfileJSONWriter.h"
     12 #include "mozilla/BaseProfilingCategory.h"
     13 #include "mozilla/HashFunctions.h"
     14 #include "mozilla/HashTable.h"
     15 #include "mozilla/Maybe.h"
     16 #include "mozilla/ProfileBufferEntryKinds.h"
     17 #include "mozilla/UniquePtr.h"
     18 #include "mozilla/Variant.h"
     19 
     20 #include <string>
     21 #include <type_traits>
     22 
     23 namespace mozilla {
     24 namespace baseprofiler {
     25 
     26 class ProfileBufferEntry {
     27 public:
     28  using KindUnderlyingType =
     29      std::underlying_type_t<::mozilla::ProfileBufferEntryKind>;
     30  using Kind = ::mozilla::ProfileBufferEntryKind;
     31 
     32  ProfileBufferEntry();
     33 
     34  static constexpr size_t kNumChars = ::mozilla::ProfileBufferEntryNumChars;
     35 
     36 private:
     37  // aString must be a static string.
     38  ProfileBufferEntry(Kind aKind, const char* aString);
     39  ProfileBufferEntry(Kind aKind, char aChars[kNumChars]);
     40  ProfileBufferEntry(Kind aKind, void* aPtr);
     41  ProfileBufferEntry(Kind aKind, double aDouble);
     42  ProfileBufferEntry(Kind aKind, int64_t aInt64);
     43  ProfileBufferEntry(Kind aKind, uint64_t aUint64);
     44  ProfileBufferEntry(Kind aKind, uint32_t aUint32);
     45  ProfileBufferEntry(Kind aKind, int aInt);
     46  ProfileBufferEntry(Kind aKind, BaseProfilerThreadId aThreadId);
     47 
     48 public:
     49 #define CTOR(KIND, TYPE, SIZE)                   \
     50  static ProfileBufferEntry KIND(TYPE aVal) {    \
     51    return ProfileBufferEntry(Kind::KIND, aVal); \
     52  }
     53  FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(CTOR)
     54 #undef CTOR
     55 
     56  Kind GetKind() const { return mKind; }
     57 
     58 #define IS_KIND(KIND, TYPE, SIZE) \
     59  bool Is##KIND() const { return mKind == Kind::KIND; }
     60  FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(IS_KIND)
     61 #undef IS_KIND
     62 
     63 private:
     64  FRIEND_TEST(ThreadProfile, InsertOneEntry);
     65  FRIEND_TEST(ThreadProfile, InsertOneEntryWithTinyBuffer);
     66  FRIEND_TEST(ThreadProfile, InsertEntriesNoWrap);
     67  FRIEND_TEST(ThreadProfile, InsertEntriesWrap);
     68  FRIEND_TEST(ThreadProfile, MemoryMeasure);
     69  friend class ProfileBuffer;
     70 
     71  Kind mKind;
     72  uint8_t mStorage[kNumChars];
     73 
     74  const char* GetString() const;
     75  void* GetPtr() const;
     76  double GetDouble() const;
     77  int GetInt() const;
     78  int64_t GetInt64() const;
     79  uint64_t GetUint64() const;
     80  BaseProfilerThreadId GetThreadId() const;
     81  void CopyCharsInto(char (&aOutArray)[kNumChars]) const;
     82 };
     83 
     84 // Packed layout: 1 byte for the tag + 8 bytes for the value.
     85 static_assert(sizeof(ProfileBufferEntry) == 9, "bad ProfileBufferEntry size");
     86 
     87 class UniqueStacks {
     88 public:
     89  struct FrameKey {
     90    explicit FrameKey(const char* aLocation)
     91        : mData(NormalFrameData{std::string(aLocation), false, 0, Nothing(),
     92                                Nothing()}) {}
     93 
     94    FrameKey(std::string&& aLocation, bool aRelevantForJS,
     95             uint64_t aInnerWindowID, const Maybe<unsigned>& aLine,
     96             const Maybe<unsigned>& aColumn,
     97             const Maybe<ProfilingCategoryPair>& aCategoryPair)
     98        : mData(NormalFrameData{aLocation, aRelevantForJS, aInnerWindowID,
     99                                aLine, aColumn, aCategoryPair}) {}
    100 
    101    FrameKey(const FrameKey& aToCopy) = default;
    102 
    103    uint32_t Hash() const;
    104    bool operator==(const FrameKey& aOther) const {
    105      return mData == aOther.mData;
    106    }
    107 
    108    struct NormalFrameData {
    109      bool operator==(const NormalFrameData& aOther) const;
    110 
    111      std::string mLocation;
    112      bool mRelevantForJS;
    113      uint64_t mInnerWindowID;
    114      Maybe<unsigned> mLine;
    115      Maybe<unsigned> mColumn;
    116      Maybe<ProfilingCategoryPair> mCategoryPair;
    117    };
    118    Variant<NormalFrameData> mData;
    119  };
    120 
    121  struct FrameKeyHasher {
    122    using Lookup = FrameKey;
    123 
    124    static HashNumber hash(const FrameKey& aLookup) {
    125      HashNumber hash = 0;
    126      if (aLookup.mData.is<FrameKey::NormalFrameData>()) {
    127        const FrameKey::NormalFrameData& data =
    128            aLookup.mData.as<FrameKey::NormalFrameData>();
    129        if (!data.mLocation.empty()) {
    130          hash = AddToHash(hash, HashString(data.mLocation.c_str()));
    131        }
    132        hash = AddToHash(hash, data.mRelevantForJS);
    133        hash = mozilla::AddToHash(hash, data.mInnerWindowID);
    134        if (data.mLine.isSome()) {
    135          hash = AddToHash(hash, *data.mLine);
    136        }
    137        if (data.mColumn.isSome()) {
    138          hash = AddToHash(hash, *data.mColumn);
    139        }
    140        if (data.mCategoryPair.isSome()) {
    141          hash = AddToHash(hash, static_cast<uint32_t>(*data.mCategoryPair));
    142        }
    143      }
    144      return hash;
    145    }
    146 
    147    static bool match(const FrameKey& aKey, const FrameKey& aLookup) {
    148      return aKey == aLookup;
    149    }
    150 
    151    static void rekey(FrameKey& aKey, const FrameKey& aNewKey) {
    152      aKey = aNewKey;
    153    }
    154  };
    155 
    156  struct StackKey {
    157    Maybe<uint32_t> mPrefixStackIndex;
    158    uint32_t mFrameIndex;
    159 
    160    explicit StackKey(uint32_t aFrame)
    161        : mFrameIndex(aFrame), mHash(HashGeneric(aFrame)) {}
    162 
    163    StackKey(const StackKey& aPrefix, uint32_t aPrefixStackIndex,
    164             uint32_t aFrame)
    165        : mPrefixStackIndex(Some(aPrefixStackIndex)),
    166          mFrameIndex(aFrame),
    167          mHash(AddToHash(aPrefix.mHash, aFrame)) {}
    168 
    169    HashNumber Hash() const { return mHash; }
    170 
    171    bool operator==(const StackKey& aOther) const {
    172      return mPrefixStackIndex == aOther.mPrefixStackIndex &&
    173             mFrameIndex == aOther.mFrameIndex;
    174    }
    175 
    176   private:
    177    HashNumber mHash;
    178  };
    179 
    180  struct StackKeyHasher {
    181    using Lookup = StackKey;
    182 
    183    static HashNumber hash(const StackKey& aLookup) { return aLookup.Hash(); }
    184 
    185    static bool match(const StackKey& aKey, const StackKey& aLookup) {
    186      return aKey == aLookup;
    187    }
    188 
    189    static void rekey(StackKey& aKey, const StackKey& aNewKey) {
    190      aKey = aNewKey;
    191    }
    192  };
    193 
    194  UniqueStacks();
    195 
    196  // Return a StackKey for aFrame as the stack's root frame (no prefix).
    197  [[nodiscard]] StackKey BeginStack(const FrameKey& aFrame);
    198 
    199  // Return a new StackKey that is obtained by appending aFrame to aStack.
    200  [[nodiscard]] StackKey AppendFrame(const StackKey& aStack,
    201                                     const FrameKey& aFrame);
    202 
    203  [[nodiscard]] uint32_t GetOrAddFrameIndex(const FrameKey& aFrame);
    204  [[nodiscard]] uint32_t GetOrAddStackIndex(const StackKey& aStack);
    205 
    206  void SpliceFrameTableElements(SpliceableJSONWriter& aWriter);
    207  void SpliceStackTableElements(SpliceableJSONWriter& aWriter);
    208 
    209  UniqueJSONStrings& UniqueStrings() {
    210    MOZ_RELEASE_ASSERT(mUniqueStrings.get());
    211    return *mUniqueStrings;
    212  }
    213 
    214 private:
    215  void StreamNonJITFrame(const FrameKey& aFrame);
    216  void StreamStack(const StackKey& aStack);
    217 
    218  UniquePtr<UniqueJSONStrings> mUniqueStrings;
    219 
    220  SpliceableChunkedJSONWriter mFrameTableWriter;
    221  HashMap<FrameKey, uint32_t, FrameKeyHasher> mFrameToIndexMap;
    222 
    223  SpliceableChunkedJSONWriter mStackTableWriter;
    224  HashMap<StackKey, uint32_t, StackKeyHasher> mStackToIndexMap;
    225 };
    226 
    227 //
    228 // Thread profile JSON Format
    229 // --------------------------
    230 //
    231 // The profile contains much duplicate information. The output JSON of the
    232 // profile attempts to deduplicate strings, frames, and stack prefixes, to cut
    233 // down on size and to increase JSON streaming speed. Deduplicated values are
    234 // streamed as indices into their respective tables.
    235 //
    236 // Further, arrays of objects with the same set of properties (e.g., samples,
    237 // frames) are output as arrays according to a schema instead of an object
    238 // with property names. A property that is not present is represented in the
    239 // array as null or undefined.
    240 //
    241 // The format of the thread profile JSON is shown by the following example
    242 // with 1 sample and 1 marker:
    243 //
    244 // {
    245 //   "name": "Foo",
    246 //   "tid": 42,
    247 //   "samples":
    248 //   {
    249 //     "schema":
    250 //     {
    251 //       "stack": 0,          /* index into stackTable */
    252 //       "time": 1,           /* number */
    253 //       "eventDelay": 2, /* number */
    254 //     },
    255 //     "data":
    256 //     [
    257 //       [ 1, 0.0, 0.0 ]      /* { stack: 1, time: 0.0, eventDelay: 0.0 } */
    258 //     ]
    259 //   },
    260 //
    261 //   "markers":
    262 //   {
    263 //     "schema":
    264 //     {
    265 //       "name": 0,           /* index into stringTable */
    266 //       "time": 1,           /* number */
    267 //       "data": 2            /* arbitrary JSON */
    268 //     },
    269 //     "data":
    270 //     [
    271 //       [ 3, 0.1 ]           /* { name: 'example marker', time: 0.1 } */
    272 //     ]
    273 //   },
    274 //
    275 //   "stackTable":
    276 //   {
    277 //     "schema":
    278 //     {
    279 //       "prefix": 0,         /* index into stackTable */
    280 //       "frame": 1           /* index into frameTable */
    281 //     },
    282 //     "data":
    283 //     [
    284 //       [ null, 0 ],         /* (root) */
    285 //       [ 0,    1 ]          /* (root) > foo.js */
    286 //     ]
    287 //   },
    288 //
    289 //   "frameTable":
    290 //   {
    291 //     "schema":
    292 //     {
    293 //       "location": 0,       /* index into stringTable */
    294 //       "relevantForJS": 1,  /* bool */
    295 //       "innerWindowID": 2,  /* inner window ID of global JS `window` object */
    296 //       "implementation": 3, /* index into stringTable */
    297 //       "line": 4,           /* number */
    298 //       "column": 5,         /* number */
    299 //       "category": 6,       /* index into profile.meta.categories */
    300 //       "subcategory": 7     /* index into
    301 //       profile.meta.categories[category].subcategories */
    302 //     },
    303 //     "data":
    304 //     [
    305 //       [ 0 ],               /* { location: '(root)' } */
    306 //       [ 1, 2 ]             /* { location: 'foo.js',
    307 //                                 implementation: 'baseline' } */
    308 //     ]
    309 //   },
    310 //
    311 //   "stringTable":
    312 //   [
    313 //     "(root)",
    314 //     "foo.js",
    315 //     "baseline",
    316 //     "example marker"
    317 //   ]
    318 // }
    319 //
    320 // Process:
    321 // {
    322 //   "name": "Bar",
    323 //   "pid": 24,
    324 //   "threads":
    325 //   [
    326 //     <0-N threads from above>
    327 //   ],
    328 //   "counters": /* includes the memory counter */
    329 //   [
    330 //     {
    331 //       "name": "qwerty",
    332 //       "category": "uiop",
    333 //       "description": "this is qwerty uiop",
    334 //       "sample_groups:
    335 //       [
    336 //         {
    337 //           "id": 42, /* number (thread id, or object identifier (tab), etc) */
    338 //           "samples:
    339 //           {
    340 //             "schema":
    341 //             {
    342 //               "time": 1,   /* number */
    343 //               "number": 2, /* number (of times the counter was touched) */
    344 //               "count": 3   /* number (total for the counter) */
    345 //             },
    346 //             "data":
    347 //             [
    348 //               [ 0.1, 1824,
    349 //                 454622 ]   /* { time: 0.1, number: 1824, count: 454622 } */
    350 //             ]
    351 //           },
    352 //         },
    353 //         /* more sample-group objects with different id's */
    354 //       ]
    355 //     },
    356 //     /* more counters */
    357 //   ],
    358 // }
    359 //
    360 
    361 }  // namespace baseprofiler
    362 }  // namespace mozilla
    363 
    364 #endif /* ndef ProfileBufferEntry_h */