Skip to content

More informations about the source of user-callback and event-listener scripts #12

Open

Description

@rackaam

We are using the LoAF api to try to pinpoint the origins of some performance issues but most of the time we don't get enough information to have something actionable.

Most of the scripts are "user-callback" or "event-listener":

{
    "name": "script",
    "entryType": "script",
    "startTime": 5038.5999999996275,
    "duration": 767,
    "invoker": "TimerHandler:setTimeout",
    "invokerType": "user-callback",
    "windowAttribution": "self",
    "executionStart": 5038.5999999996275,
    "forcedStyleAndLayoutDuration": 111,
    "pauseDuration": 0,
    "sourceURL": "",
    "sourceFunctionName": "",
    "sourceCharPosition": -1
}
{
    "name": "script",
    "entryType": "script",
    "startTime": 5867.299999998882,
    "duration": 35,
    "invoker": "XMLHttpRequest.onreadystatechange",
    "invokerType": "event-listener",
    "windowAttribution": "self",
    "executionStart": 5867.299999998882,
    "forcedStyleAndLayoutDuration": 0,
    "pauseDuration": 0,
    "sourceURL": "",
    "sourceFunctionName": "",
    "sourceCharPosition": -1
}

Is there any way to get more information such as where the setTimeout has been called?

Activity

  1. mmocny commented on May 28, 2024

    @mmocny
    Contributor

    Might be related to #11

  2. jinja12 commented on Jun 10, 2024

    @jinja12

    Requesting to review this once (@mmocny @tunetheweb @noamr), the current LoAF entries obtained from a simple PerformanceObserver observing for Long Animation Frames like:

        const REPORTING_THRESHOLD_MS = 150;
        const observer = new PerformanceObserver((list) => {
          for (const entry of list.getEntries()) {
            if (
              entry.duration > REPORTING_THRESHOLD_MS &&
              (entry as any).firstUIEventTimestamp > 0
            ) {
              console.log("Long task detected:", entry)
              analyzePerformanceEntry(entry);
            }
          }
        });
    
        observer.observe({ type: "long-animation-frame", buffered: true });

    To get something more actionable, if we could get the exact function (name) delaying the main thread it would be best (case where we automating this process of finding action items. The sourceFunctionName and sourceURL are not really helpful when working with a bundled code (eg: like in ReactJS, NextJS). Is there a way to get more insights into the same apart from the above so that finding actionable items is easier ? I am aware of the method specified here: "#3 (comment)" , but something more useful in scenarios such as above would be nice. Continuation of Issue: #3

  3. tunetheweb commented on Jun 10, 2024

    @tunetheweb
    Member

    @jinja12 that seems directly related to #3 as you mention, rather than the issue @rackaam raised here?

  4. jinja12 commented on Jun 10, 2024

    @jinja12

    Yeah, I added it here since I too want to get more information regarding the entry point of the function responsible for the long task blocking (not specific to setTimeout)

  5. noamr commented on Jun 10, 2024

    @noamr
    Collaborator

    This is more related to #11 than to #3. It's most likely setTimeout(code_as_string, timeout) and xhr.onreadystatechange = handler_as_string, both of which don't have an obvious source location. We can perhaps provide an elided version of the source code itself, or an md5 digest?

  6. jinja12 commented on Jun 10, 2024

    @jinja12

    Right, is there an existing way to get the source location of the function calling the long task in bundled packages in cases excluding the one you mention? For example:

    function blockMainThread(duration: number) {
        const start = Date.now();
        while (Date.now() - start < duration) {
          // Busy-wait loop to block the main thread
        }
    }
    
    // A simple form embedded on a page, which when submitted triggers a 3s main thread block
    const form = document.getElementById('testForm');
    
    form.onsubmit = (event) => {
      event.preventDefault();
      blockMainThread(3000); // 3-second blocking operation
    };
  7. noamr commented on Jun 10, 2024

    @noamr
    Collaborator

    Right, is there an existing way to get the source location of the function calling the long task in bundled packages in cases excluding the one you mention? For example:

    function blockMainThread(duration: number) {
        const start = Date.now();
        while (Date.now() - start < duration) {
          // Busy-wait loop to block the main thread
        }
    }
    
    // A simple form embedded on a page, which when submitted triggers a 3s main thread block
    const form = document.getElementById('testForm');
    
    form.onsubmit = (event) => {
      event.preventDefault();
      blockMainThread(3000); // 3-second blocking operation
    };

    In this case sourceLocation would give you the location of the arrow function you passed to onsubmit. There is no existing low-overhead way to give you the source location of blockMainThread, as that would require a full stack trace which is a very heavy operation, but as per #11 you can wrap it in user timing and perhaps we can at some point provide some mechanism for conditional user-timing or special function annotation for this purpose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions