tor-browser

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

CacheIRHealth.cpp (13515B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 #ifdef JS_CACHEIR_SPEW
      7 
      8 #  include "jit/CacheIRHealth.h"
      9 
     10 #  include "gc/Zone.h"
     11 #  include "jit/BaselineIC.h"
     12 #  include "jit/CacheIRCompiler.h"
     13 #  include "jit/JitScript.h"
     14 #  include "js/ColumnNumber.h"  // JS::LimitedColumnNumberOneOrigin
     15 #  include "vm/JSScript.h"
     16 
     17 #  include "vm/JSObject-inl.h"
     18 #  include "vm/Realm-inl.h"
     19 
     20 using namespace js;
     21 using namespace js::jit;
     22 
     23 // TODO: Refine how we assign happiness based on total health score.
     24 CacheIRHealth::Happiness CacheIRHealth::determineStubHappiness(
     25    uint32_t stubHealthScore) {
     26  if (stubHealthScore >= 30) {
     27    return Sad;
     28  }
     29 
     30  if (stubHealthScore >= 20) {
     31    return MediumSad;
     32  }
     33 
     34  if (stubHealthScore >= 10) {
     35    return MediumHappy;
     36  }
     37 
     38  return Happy;
     39 }
     40 
     41 CacheIRHealth::Happiness CacheIRHealth::spewStubHealth(
     42    AutoStructuredSpewer& spew, ICCacheIRStub* stub) {
     43  const CacheIRStubInfo* stubInfo = stub->stubInfo();
     44  CacheIRReader stubReader(stubInfo);
     45  uint32_t totalStubHealth = 0;
     46  spew->beginListProperty("cacheIROps");
     47  while (stubReader.more()) {
     48    CacheOp op = stubReader.readOp();
     49    uint32_t opHealth = CacheIROpHealth[size_t(op)];
     50    uint32_t argLength = CacheIROpInfos[size_t(op)].argLength;
     51    const char* opName = CacheIROpNames[size_t(op)];
     52 
     53    spew->beginObject();
     54    if (opHealth == UINT32_MAX) {
     55      spew->property("unscoredOp", opName);
     56    } else {
     57      spew->property("cacheIROp", opName);
     58      spew->property("opHealth", opHealth);
     59      totalStubHealth += opHealth;
     60    }
     61    spew->endObject();
     62 
     63    stubReader.skip(argLength);
     64  }
     65  spew->endList();  // cacheIROps
     66 
     67  spew->property("stubHealth", totalStubHealth);
     68 
     69  Happiness stubHappiness = determineStubHappiness(totalStubHealth);
     70  spew->property("stubHappiness", stubHappiness);
     71 
     72  return stubHappiness;
     73 }
     74 
     75 BaseScript* CacheIRHealth::maybeExtractBaseScript(JSContext* cx, Shape* shape) {
     76  TaggedProto taggedProto = shape->base()->proto();
     77  if (!taggedProto.isObject()) {
     78    return nullptr;
     79  }
     80  Value cval;
     81  JSObject* proto = taggedProto.toObject();
     82  AutoRealm ar(cx, proto);
     83  if (!GetPropertyPure(cx, proto, NameToId(cx->names().constructor), &cval)) {
     84    return nullptr;
     85  }
     86  if (!IsFunctionObject(cval)) {
     87    return nullptr;
     88  }
     89  JSFunction& jsfun = cval.toObject().as<JSFunction>();
     90  if (!jsfun.hasBaseScript()) {
     91    return nullptr;
     92  }
     93  return jsfun.baseScript();
     94 }
     95 
     96 void CacheIRHealth::spewShapeInformation(AutoStructuredSpewer& spew,
     97                                         JSContext* cx, ICStub* stub) {
     98  bool shapesStarted = false;
     99  const CacheIRStubInfo* stubInfo = stub->toCacheIRStub()->stubInfo();
    100  size_t offset = 0;
    101  uint32_t fieldIndex = 0;
    102 
    103  while (stubInfo->fieldType(fieldIndex) != StubField::Type::Limit) {
    104    if (stubInfo->fieldType(fieldIndex) == StubField::Type::Shape) {
    105      Shape* shape = reinterpret_cast<Shape*>(
    106          stubInfo->getStubRawWord(stub->toCacheIRStub(), offset));
    107      if (!shapesStarted) {
    108        shapesStarted = true;
    109        spew->beginListProperty("shapes");
    110      }
    111 
    112      const PropMap* propMap =
    113          shape->isNative() ? shape->asNative().propMap() : nullptr;
    114      if (propMap) {
    115        spew->beginObject();
    116        {
    117          if (!propMap->isDictionary()) {
    118            uint32_t mapLength = shape->asNative().propMapLength();
    119            if (mapLength) {
    120              PropertyKey lastKey = shape->asNative().lastProperty().key();
    121              if (lastKey.isInt()) {
    122                spew->property("lastProperty", lastKey.toInt());
    123              } else if (lastKey.isString()) {
    124                JSString* str = lastKey.toString();
    125                if (str && str->isLinear()) {
    126                  spew->property("lastProperty", &str->asLinear());
    127                }
    128              } else {
    129                MOZ_ASSERT(lastKey.isSymbol());
    130                JSString* str = lastKey.toSymbol()->description();
    131                if (str && str->isLinear()) {
    132                  spew->property("lastProperty", &str->asLinear());
    133                }
    134              }
    135            }
    136            spew->property("totalKeys", propMap->approximateEntryCount());
    137            BaseScript* baseScript = maybeExtractBaseScript(cx, shape);
    138            if (baseScript) {
    139              spew->beginObjectProperty("shapeAllocSite");
    140              {
    141                spew->property("filename", baseScript->filename());
    142                spew->property("line", baseScript->lineno());
    143                spew->property("column", baseScript->column().oneOriginValue());
    144              }
    145              spew->endObject();
    146            }
    147          }
    148        }
    149        spew->endObject();
    150      }
    151    }
    152    offset += StubField::sizeInBytes(stubInfo->fieldType(fieldIndex));
    153    fieldIndex++;
    154  }
    155 
    156  if (shapesStarted) {
    157    spew->endList();
    158  }
    159 }
    160 
    161 bool CacheIRHealth::spewNonFallbackICInformation(AutoStructuredSpewer& spew,
    162                                                 JSContext* cx,
    163                                                 ICStub* firstStub,
    164                                                 Happiness* entryHappiness) {
    165  const CacheIRStubInfo* stubInfo = firstStub->toCacheIRStub()->stubInfo();
    166  Vector<bool, 8, SystemAllocPolicy> sawDistinctValueAtFieldIndex;
    167 
    168  bool sawNonZeroCount = false;
    169  bool sawDifferentCacheIRStubs = false;
    170  ICStub* stub = firstStub;
    171 
    172  spew->beginListProperty("stubs");
    173  while (stub && !stub->isFallback()) {
    174    spew->beginObject();
    175    {
    176      Happiness stubHappiness = spewStubHealth(spew, stub->toCacheIRStub());
    177      if (stubHappiness < *entryHappiness) {
    178        *entryHappiness = stubHappiness;
    179      }
    180 
    181      spewShapeInformation(spew, cx, stub);
    182 
    183      ICStub* nextStub = stub->toCacheIRStub()->next();
    184      if (!nextStub->isFallback()) {
    185        if (nextStub->enteredCount() > 0) {
    186          // More than one stub has a hit count greater than zero.
    187          // This is sad because we do not Warp transpile in this case.
    188          *entryHappiness = Sad;
    189          sawNonZeroCount = true;
    190        }
    191 
    192        if (nextStub->toCacheIRStub()->stubInfo() != stubInfo) {
    193          sawDifferentCacheIRStubs = true;
    194        }
    195 
    196        // If there are multiple stubs with non zero hit counts and if all
    197        // of the stubs have equivalent CacheIR, then keep track of how many
    198        // distinct stub field values are seen for each field index.
    199        if (sawNonZeroCount && !sawDifferentCacheIRStubs) {
    200          uint32_t fieldIndex = 0;
    201          size_t offset = 0;
    202 
    203          while (stubInfo->fieldType(fieldIndex) != StubField::Type::Limit) {
    204            if (sawDistinctValueAtFieldIndex.length() <= fieldIndex) {
    205              if (!sawDistinctValueAtFieldIndex.append(false)) {
    206                return false;
    207              }
    208            }
    209 
    210            if (StubField::sizeIsWord(stubInfo->fieldType(fieldIndex))) {
    211              uintptr_t firstRaw =
    212                  stubInfo->getStubRawWord(firstStub->toCacheIRStub(), offset);
    213              uintptr_t nextRaw =
    214                  stubInfo->getStubRawWord(nextStub->toCacheIRStub(), offset);
    215              if (firstRaw != nextRaw) {
    216                sawDistinctValueAtFieldIndex[fieldIndex] = true;
    217              }
    218            } else {
    219              MOZ_ASSERT(
    220                  StubField::sizeIsInt64(stubInfo->fieldType(fieldIndex)));
    221              int64_t firstRaw =
    222                  stubInfo->getStubRawInt64(firstStub->toCacheIRStub(), offset);
    223              int64_t nextRaw =
    224                  stubInfo->getStubRawInt64(nextStub->toCacheIRStub(), offset);
    225 
    226              if (firstRaw != nextRaw) {
    227                sawDistinctValueAtFieldIndex[fieldIndex] = true;
    228              }
    229            }
    230 
    231            offset += StubField::sizeInBytes(stubInfo->fieldType(fieldIndex));
    232            fieldIndex++;
    233          }
    234        }
    235      }
    236 
    237      spew->property("hitCount", stub->enteredCount());
    238      stub = nextStub;
    239    }
    240    spew->endObject();
    241  }
    242  spew->endList();  // stubs
    243 
    244  // If more than one CacheIR stub has an entered count greater than
    245  // zero and all the stubs have equivalent CacheIR, then spew
    246  // the information collected about the stub fields across the IC.
    247  if (sawNonZeroCount && !sawDifferentCacheIRStubs) {
    248    spew->beginListProperty("stubFields");
    249    for (size_t i = 0; i < sawDistinctValueAtFieldIndex.length(); i++) {
    250      spew->beginObject();
    251      {
    252        spew->property("fieldType", uint8_t(stubInfo->fieldType(i)));
    253        spew->property("sawDistinctFieldValues",
    254                       sawDistinctValueAtFieldIndex[i]);
    255      }
    256      spew->endObject();
    257    }
    258    spew->endList();
    259  }
    260 
    261  return true;
    262 }
    263 
    264 bool CacheIRHealth::spewICEntryHealth(AutoStructuredSpewer& spew, JSContext* cx,
    265                                      HandleScript script, ICEntry* entry,
    266                                      ICFallbackStub* fallback, jsbytecode* pc,
    267                                      JSOp op, Happiness* entryHappiness) {
    268  spew->property("op", CodeName(op));
    269 
    270  // TODO: If a perf issue arises, look into improving the SrcNotes
    271  // API call below.
    272  JS::LimitedColumnNumberOneOrigin column;
    273  spew->property("lineno", PCToLineNumber(script, pc, &column));
    274  spew->property("column", column.oneOriginValue());
    275 
    276  ICStub* firstStub = entry->firstStub();
    277  if (!firstStub->isFallback()) {
    278    if (!spewNonFallbackICInformation(spew, cx, firstStub, entryHappiness)) {
    279      return false;
    280    }
    281  }
    282 
    283  if (fallback->state().mode() != ICState::Mode::Specialized) {
    284    *entryHappiness = Sad;
    285  }
    286 
    287  spew->property("entryHappiness", uint8_t(*entryHappiness));
    288 
    289  spew->property("mode", uint8_t(fallback->state().mode()));
    290 
    291  spew->property("fallbackCount", fallback->enteredCount());
    292 
    293  return true;
    294 }
    295 
    296 void CacheIRHealth::spewScriptFinalWarmUpCount(JSContext* cx,
    297                                               const char* filename,
    298                                               JSScript* script,
    299                                               uint32_t warmUpCount) {
    300  AutoStructuredSpewer spew(cx, SpewChannel::CacheIRHealthReport, nullptr);
    301  if (!spew) {
    302    return;
    303  }
    304 
    305  spew->property("filename", filename);
    306  spew->property("line", script->lineno());
    307  spew->property("column", script->column().oneOriginValue());
    308  spew->property("finalWarmUpCount", warmUpCount);
    309 }
    310 
    311 static bool addScriptToFinalWarmUpCountMap(JSContext* cx, HandleScript script) {
    312  // Create Zone::scriptFilenameMap if necessary.
    313  JS::Zone* zone = script->zone();
    314  if (!zone->scriptFinalWarmUpCountMap) {
    315    auto map = MakeUnique<ScriptFinalWarmUpCountMap>();
    316    if (!map) {
    317      return false;
    318    }
    319 
    320    zone->scriptFinalWarmUpCountMap = std::move(map);
    321  }
    322 
    323  SharedImmutableString sfilename =
    324      SharedImmutableStringsCache::getSingleton().getOrCreate(
    325          script->filename(), strlen(script->filename()));
    326  if (!sfilename) {
    327    ReportOutOfMemory(cx);
    328    return false;
    329  }
    330 
    331  if (!zone->scriptFinalWarmUpCountMap->put(
    332          script, std::make_tuple(uint32_t(0), std::move(sfilename)))) {
    333    ReportOutOfMemory(cx);
    334    return false;
    335  }
    336 
    337  script->setNeedsFinalWarmUpCount();
    338  return true;
    339 }
    340 
    341 void CacheIRHealth::healthReportForIC(JSContext* cx, ICEntry* entry,
    342                                      ICFallbackStub* fallback,
    343                                      HandleScript script,
    344                                      SpewContext context) {
    345  AutoStructuredSpewer spew(cx, SpewChannel::CacheIRHealthReport, script);
    346  if (!spew) {
    347    return;
    348  }
    349 
    350  if (!addScriptToFinalWarmUpCountMap(cx, script)) {
    351    cx->recoverFromOutOfMemory();
    352    return;
    353  }
    354  spew->property("spewContext", uint8_t(context));
    355 
    356  jsbytecode* op = script->offsetToPC(fallback->pcOffset());
    357  JSOp jsOp = JSOp(*op);
    358 
    359  Happiness entryHappiness = Happy;
    360  if (!spewICEntryHealth(spew, cx, script, entry, fallback, op, jsOp,
    361                         &entryHappiness)) {
    362    cx->recoverFromOutOfMemory();
    363    return;
    364  }
    365  MOZ_ASSERT(entryHappiness == Sad);
    366 }
    367 
    368 void CacheIRHealth::healthReportForScript(JSContext* cx, HandleScript script,
    369                                          SpewContext context) {
    370  jit::JitScript* jitScript = script->maybeJitScript();
    371  if (!jitScript) {
    372    return;
    373  }
    374 
    375  AutoStructuredSpewer spew(cx, SpewChannel::CacheIRHealthReport, script);
    376  if (!spew) {
    377    return;
    378  }
    379 
    380  if (!addScriptToFinalWarmUpCountMap(cx, script)) {
    381    cx->recoverFromOutOfMemory();
    382    return;
    383  }
    384 
    385  spew->property("spewContext", uint8_t(context));
    386 
    387  spew->beginListProperty("entries");
    388 
    389  Happiness scriptHappiness = Happy;
    390 
    391  for (size_t i = 0; i < jitScript->numICEntries(); i++) {
    392    ICEntry& entry = jitScript->icEntry(i);
    393    ICFallbackStub* fallback = jitScript->fallbackStub(i);
    394    jsbytecode* pc = script->offsetToPC(fallback->pcOffset());
    395    JSOp op = JSOp(*pc);
    396 
    397    spew->beginObject();
    398    Happiness entryHappiness = Happy;
    399    if (!spewICEntryHealth(spew, cx, script, &entry, fallback, pc, op,
    400                           &entryHappiness)) {
    401      cx->recoverFromOutOfMemory();
    402      return;
    403    }
    404    if (entryHappiness < scriptHappiness) {
    405      scriptHappiness = entryHappiness;
    406    }
    407    spew->endObject();
    408  }
    409 
    410  spew->endList();  // entries
    411 
    412  spew->property("scriptHappiness", uint8_t(scriptHappiness));
    413 }
    414 
    415 #endif /* JS_CACHEIR_SPEW */