tor-browser

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

profile-utils.js (4897B)


      1 (function(global) {
      2  const TEST_SAMPLE_INTERVAL = 10;
      3  const ENSURE_SAMPLE_SPIN_WAIT_MS = 500;
      4 
      5  function forceSample() {
      6    // Spin for |TEST_SAMPLE_INTERVAL + 500|ms to ensure that a sample occurs
      7    // before this function returns. As periodic sampling is enforced by a
      8    // SHOULD clause, it is indeed testable.
      9    //
     10    // More reliable sampling will be handled in a future testdriver RFC
     11    // (https://github.com/web-platform-tests/rfcs/pull/81).
     12    for (const deadline = performance.now() + TEST_SAMPLE_INTERVAL +
     13             ENSURE_SAMPLE_SPIN_WAIT_MS;
     14         performance.now() < deadline;)
     15      ;
     16  }
     17 
     18  // Creates a new profile that captures the execution of when the given
     19  // function calls the `sample` function passed to it.
     20  async function profileFunction(func) {
     21    const profiler = new Profiler({
     22      sampleInterval: TEST_SAMPLE_INTERVAL,
     23      maxBufferSize: Number.MAX_SAFE_INTEGER,
     24    });
     25 
     26    func(() => forceSample());
     27 
     28    const trace = await profiler.stop();
     29 
     30    // Sanity check ensuring that we captured a sample.
     31    assert_greater_than(trace.resources.length, 0);
     32    assert_greater_than(trace.frames.length, 0);
     33    assert_greater_than(trace.stacks.length, 0);
     34    assert_greater_than(trace.samples.length, 0);
     35 
     36    return trace;
     37  }
     38 
     39  async function testFunction(func, frame) {
     40    const trace = await profileFunction(func);
     41    assert_true(containsFrame(trace, frame), 'trace contains frame');
     42  }
     43 
     44  function substackMatches(trace, stackId, expectedStack) {
     45    if (expectedStack.length === 0) {
     46      return true;
     47    }
     48    if (stackId === undefined) {
     49      return false;
     50    }
     51 
     52    const stackElem = trace.stacks[stackId];
     53    const expectedFrame = expectedStack[0];
     54 
     55    if (!frameMatches(trace.frames[stackElem.frameId], expectedFrame)) {
     56      return false;
     57    }
     58    return substackMatches(trace, stackElem.parentId, expectedStack.slice(1));
     59  }
     60 
     61  // Returns true if the trace contains a frame matching the given specification.
     62  // We define a "match" as follows: a frame A matches an expectation E if (and
     63  // only if) for each field of E, A has the same value.
     64  function containsFrame(trace, expectedFrame) {
     65    return trace.frames.find(frame => {
     66      return frameMatches(frame, expectedFrame);
     67    }) !== undefined;
     68  }
     69 
     70  // Returns true if a trace contains a substack in one of its samples, ordered
     71  // leaf to root.
     72  function containsSubstack(trace, expectedStack) {
     73    return trace.samples.find(sample => {
     74      let stackId = sample.stackId;
     75      while (stackId !== undefined) {
     76        if (substackMatches(trace, stackId, expectedStack)) {
     77          return true;
     78        }
     79        stackId = trace.stacks[stackId].parentId;
     80      }
     81      return false;
     82    }) !== undefined;
     83  }
     84 
     85  function containsResource(trace, expectedResource) {
     86    return trace.resources.includes(expectedResource);
     87  }
     88 
     89  // Returns true if a trace contains a sample matching the given specification.
     90  // We define a "match" as follows: a sample A matches an expectation E if (and
     91  // only if) for each field of E, A has the same value.
     92  function containsSample(trace, expectedSample) {
     93    return trace.samples.find(sample => {
     94      return sampleMatches(sample, expectedSample);
     95    }) !== undefined;
     96  }
     97 
     98  // Compares each set field of `expected` against the given frame `actual`.
     99  function sampleMatches(actual, expected) {
    100    return (expected.timestamp === undefined ||
    101            expected.timestamp === actual.timestamp) &&
    102        (expected.stackId === undefined ||
    103         expected.stackId === actual.stackId) &&
    104        (expected.marker === undefined || expected.marker === actual.marker);
    105  }
    106 
    107  // Compares each set field of `expected` against the given frame `actual`.
    108  function frameMatches(actual, expected) {
    109    return (expected.name === undefined || expected.name === actual.name) &&
    110           (expected.resourceId === undefined || expected.resourceId === actual.resourceId) &&
    111           (expected.line === undefined || expected.line === actual.line) &&
    112           (expected.column === undefined || expected.column === actual.column);
    113  }
    114 
    115  function forceSampleFrame(frame) {
    116    const channel = new MessageChannel();
    117    const replyPromise = new Promise(res => {
    118      channel.port1.onmessage = res;
    119    });
    120    frame.postMessage('', '*', [channel.port2]);
    121    return replyPromise;
    122  }
    123 
    124  window.addEventListener('message', message => {
    125    // Force sample in response to messages received.
    126    (function sampleFromMessage() {
    127      ProfileUtils.forceSample();
    128      message.ports[0].postMessage('');
    129    })();
    130  });
    131 
    132  global.ProfileUtils = {
    133    // Capturing
    134    profileFunction,
    135    forceSample,
    136 
    137    // Containment checks
    138    containsFrame,
    139    containsSubstack,
    140    containsResource,
    141    containsSample,
    142 
    143    // Cross-frame sampling
    144    forceSampleFrame,
    145 
    146    // Assertions
    147    testFunction,
    148  };
    149 })(this);