tor-browser

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

perfutils.js (3490B)


      1 "use strict";
      2 
      3 const formatNumber = new Intl.NumberFormat("en-US", {
      4  maximumSignificantDigits: 4,
      5 }).format;
      6 
      7 /**
      8 * Given a map from test names to arrays of results, report perfherder metrics
      9 * and log full results.
     10 */
     11 function reportMetrics(journal) {
     12  let metrics = {};
     13  let text = "\nResults (ms)\n";
     14 
     15  const names = Object.keys(journal);
     16  const prefixLen = 1 + Math.max(...names.map(str => str.length));
     17 
     18  for (const name in journal) {
     19    const med = median(journal[name]);
     20    text += (name + ":").padEnd(prefixLen, " ") + stringify(journal[name]);
     21    text += "   median " + formatNumber(med) + "\n";
     22    metrics[name] = med;
     23  }
     24 
     25  dump(text);
     26  info("perfMetrics", JSON.stringify(metrics));
     27 }
     28 
     29 function median(arr) {
     30  arr = [...arr].sort((a, b) => a - b);
     31  const mid = Math.floor(arr.length / 2);
     32 
     33  if (arr.length % 2) {
     34    return arr[mid];
     35  }
     36 
     37  return (arr[mid - 1] + arr[mid]) / 2;
     38 }
     39 
     40 function stringify(arr) {
     41  function pad(str) {
     42    str = str.padStart(7, " ");
     43    if (str[0] != " ") {
     44      str = " " + str;
     45    }
     46    return str;
     47  }
     48 
     49  return arr.reduce((acc, elem) => acc + pad(formatNumber(elem)), "");
     50 }
     51 
     52 async function startProfiler() {
     53  let script = SpecialPowers.loadChromeScript(async () => {
     54    // See profiler doc via: $ MOZ_PROFILER_HELP=1 ./mach run
     55    const settings = {
     56      features: ["nomarkerstacks", "nostacksampling"],
     57      threads: ["GeckoMain", "IPDL Background"],
     58    };
     59 
     60    await Services.profiler.StartProfiler(
     61      settings.entries,
     62      settings.interval,
     63      settings.features,
     64      settings.threads
     65    );
     66 
     67    sendAsyncMessage("started");
     68  });
     69 
     70  await script.promiseOneMessage("started");
     71  script.destroy();
     72 }
     73 
     74 /**
     75 * Returns profiler data
     76 * https://github.com/firefox-devtools/profiler/blob/main/docs-developer/gecko-profile-format.md
     77 */
     78 async function stopProfiler() {
     79  let script = SpecialPowers.loadChromeScript(async () => {
     80    await Services.profiler.Pause();
     81    const profileData = await Services.profiler.getProfileDataAsync();
     82    await Services.profiler.StopProfiler();
     83    sendAsyncMessage("done", profileData);
     84  });
     85 
     86  const profile = await script.promiseOneMessage("done");
     87  script.destroy();
     88  return profile;
     89 }
     90 
     91 /**
     92 * Look through profiler results for markers with name in names.
     93 * Return the cumulative duration in ms.
     94 */
     95 function inspectProfile(pdata, names) {
     96  let unseen = new Set(names);
     97  let duration = inspectProfileInternal(pdata, new Set(unseen), unseen);
     98  // Error if we fail to see each name at least once
     99  is(
    100    unseen.size,
    101    0,
    102    `${unseen.size} missing markers ` +
    103      [...unseen].join(", ") +
    104      " (If this fails, check threads of interest in settings.threads)"
    105  );
    106  return duration;
    107 }
    108 
    109 function inspectProfileInternal(pdata, names, unseen) {
    110  let duration = 0;
    111 
    112  for (let thread of pdata.threads) {
    113    const nameIdx = thread.markers.schema.name;
    114    const startTimeIdx = thread.markers.schema.startTime;
    115    const endTimeIdx = thread.markers.schema.endTime;
    116 
    117    for (let m of thread.markers.data) {
    118      let markerName = thread.stringTable[m[nameIdx]];
    119 
    120      if (names.has(markerName)) {
    121        let d = m[endTimeIdx] - m[startTimeIdx];
    122        duration += d;
    123        info(`marker ${markerName}: ${formatNumber(d)} ms`);
    124        unseen.delete(markerName);
    125      }
    126    }
    127  }
    128 
    129  for (let process of pdata.processes) {
    130    // Look for markers in child processes
    131    duration += inspectProfileInternal(process, names, unseen);
    132  }
    133 
    134  return duration;
    135 }