tor-browser

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

cord_analysis.cc (6332B)


      1 // Copyright 2021 The Abseil Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     https://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "absl/strings/cord_analysis.h"
     16 
     17 #include <cassert>
     18 #include <cstddef>
     19 #include <cstdint>
     20 #include <unordered_set>
     21 
     22 #include "absl/base/config.h"
     23 #include "absl/base/nullability.h"
     24 #include "absl/strings/internal/cord_data_edge.h"
     25 #include "absl/strings/internal/cord_internal.h"
     26 #include "absl/strings/internal/cord_rep_btree.h"
     27 #include "absl/strings/internal/cord_rep_crc.h"
     28 
     29 namespace absl {
     30 ABSL_NAMESPACE_BEGIN
     31 namespace cord_internal {
     32 namespace {
     33 
     34 // Accounting mode for analyzing memory usage.
     35 enum class Mode { kFairShare, kTotal, kTotalMorePrecise };
     36 
     37 // CordRepRef holds a `const CordRep*` reference in rep, and depending on mode,
     38 // holds a 'fraction' representing a cumulative inverse refcount weight.
     39 template <Mode mode>
     40 struct CordRepRef {
     41  // Instantiates a CordRepRef instance.
     42  explicit CordRepRef(absl::Nonnull<const CordRep*> r) : rep(r) {}
     43 
     44  // Creates a child reference holding the provided child.
     45  // Overloaded to add cumulative reference count for kFairShare.
     46  CordRepRef Child(absl::Nonnull<const CordRep*> child) const {
     47    return CordRepRef(child);
     48  }
     49 
     50  absl::Nonnull<const CordRep*> rep;
     51 };
     52 
     53 // RawUsage holds the computed total number of bytes.
     54 template <Mode mode>
     55 struct RawUsage {
     56  size_t total = 0;
     57 
     58  // Add 'size' to total, ignoring the CordRepRef argument.
     59  void Add(size_t size, CordRepRef<mode>) { total += size; }
     60 };
     61 
     62 // Overloaded representation of RawUsage that tracks the set of objects
     63 // counted, and avoids double-counting objects referenced more than once
     64 // by the same Cord.
     65 template <>
     66 struct RawUsage<Mode::kTotalMorePrecise> {
     67  size_t total = 0;
     68  // TODO(b/289250880): Replace this with a flat_hash_set.
     69  std::unordered_set<absl::Nonnull<const CordRep*>> counted;
     70 
     71  void Add(size_t size, CordRepRef<Mode::kTotalMorePrecise> repref) {
     72    if (counted.insert(repref.rep).second) {
     73      total += size;
     74    }
     75  }
     76 };
     77 
     78 // Returns n / refcount avoiding a div for the common refcount == 1.
     79 template <typename refcount_t>
     80 double MaybeDiv(double d, refcount_t refcount) {
     81  return refcount == 1 ? d : d / refcount;
     82 }
     83 
     84 // Overloaded 'kFairShare' specialization for CordRepRef. This class holds a
     85 // `fraction` value which represents a cumulative inverse refcount weight.
     86 // For example, a top node with a reference count of 2 will have a fraction
     87 // value of 1/2 = 0.5, representing the 'fair share' of memory it references.
     88 // A node below such a node with a reference count of 5 then has a fraction of
     89 // 0.5 / 5 = 0.1 representing the fair share of memory below that node, etc.
     90 template <>
     91 struct CordRepRef<Mode::kFairShare> {
     92  // Creates a CordRepRef with the provided rep and top (parent) fraction.
     93  explicit CordRepRef(absl::Nonnull<const CordRep*> r, double frac = 1.0)
     94      : rep(r), fraction(MaybeDiv(frac, r->refcount.Get())) {}
     95 
     96  // Returns a CordRepRef with a fraction of `this->fraction / child.refcount`
     97  CordRepRef Child(absl::Nonnull<const CordRep*> child) const {
     98    return CordRepRef(child, fraction);
     99  }
    100 
    101  absl::Nonnull<const CordRep*> rep;
    102  double fraction;
    103 };
    104 
    105 // Overloaded 'kFairShare' specialization for RawUsage
    106 template <>
    107 struct RawUsage<Mode::kFairShare> {
    108  double total = 0;
    109 
    110  // Adds `size` multiplied by `rep.fraction` to the total size.
    111  void Add(size_t size, CordRepRef<Mode::kFairShare> rep) {
    112    total += static_cast<double>(size) * rep.fraction;
    113  }
    114 };
    115 
    116 // Computes the estimated memory size of the provided data edge.
    117 // External reps are assumed 'heap allocated at their exact size'.
    118 template <Mode mode>
    119 void AnalyzeDataEdge(CordRepRef<mode> rep, RawUsage<mode>& raw_usage) {
    120  assert(IsDataEdge(rep.rep));
    121 
    122  // Consume all substrings
    123  if (rep.rep->tag == SUBSTRING) {
    124    raw_usage.Add(sizeof(CordRepSubstring), rep);
    125    rep = rep.Child(rep.rep->substring()->child);
    126  }
    127 
    128  // Consume FLAT / EXTERNAL
    129  const size_t size =
    130      rep.rep->tag >= FLAT
    131          ? rep.rep->flat()->AllocatedSize()
    132          : rep.rep->length + sizeof(CordRepExternalImpl<intptr_t>);
    133  raw_usage.Add(size, rep);
    134 }
    135 
    136 // Computes the memory size of the provided Btree tree.
    137 template <Mode mode>
    138 void AnalyzeBtree(CordRepRef<mode> rep, RawUsage<mode>& raw_usage) {
    139  raw_usage.Add(sizeof(CordRepBtree), rep);
    140  const CordRepBtree* tree = rep.rep->btree();
    141  if (tree->height() > 0) {
    142    for (CordRep* edge : tree->Edges()) {
    143      AnalyzeBtree(rep.Child(edge), raw_usage);
    144    }
    145  } else {
    146    for (CordRep* edge : tree->Edges()) {
    147      AnalyzeDataEdge(rep.Child(edge), raw_usage);
    148    }
    149  }
    150 }
    151 
    152 template <Mode mode>
    153 size_t GetEstimatedUsage(absl::Nonnull<const CordRep*> rep) {
    154  // Zero initialized memory usage totals.
    155  RawUsage<mode> raw_usage;
    156 
    157  // Capture top level node and refcount into a CordRepRef.
    158  CordRepRef<mode> repref(rep);
    159 
    160  // Consume the top level CRC node if present.
    161  if (repref.rep->tag == CRC) {
    162    raw_usage.Add(sizeof(CordRepCrc), repref);
    163    if (repref.rep->crc()->child == nullptr) {
    164      return static_cast<size_t>(raw_usage.total);
    165    }
    166    repref = repref.Child(repref.rep->crc()->child);
    167  }
    168 
    169  if (IsDataEdge(repref.rep)) {
    170    AnalyzeDataEdge(repref, raw_usage);
    171  } else if (repref.rep->tag == BTREE) {
    172    AnalyzeBtree(repref, raw_usage);
    173  } else {
    174    assert(false);
    175  }
    176 
    177  return static_cast<size_t>(raw_usage.total);
    178 }
    179 
    180 }  // namespace
    181 
    182 size_t GetEstimatedMemoryUsage(absl::Nonnull<const CordRep*> rep) {
    183  return GetEstimatedUsage<Mode::kTotal>(rep);
    184 }
    185 
    186 size_t GetEstimatedFairShareMemoryUsage(absl::Nonnull<const CordRep*> rep) {
    187  return GetEstimatedUsage<Mode::kFairShare>(rep);
    188 }
    189 
    190 size_t GetMorePreciseMemoryUsage(absl::Nonnull<const CordRep*> rep) {
    191  return GetEstimatedUsage<Mode::kTotalMorePrecise>(rep);
    192 }
    193 
    194 }  // namespace cord_internal
    195 ABSL_NAMESPACE_END
    196 }  // namespace absl