Understanding javascript cpu profiles












0















Google Chrome and also the NodeJs inspector allow to generate a cpu profile with the following JSON structure:



excerpt



{
"nodes": [
{
"callFrame": {
"functionName": "(root)",
"scriptId": "0",
"url": "",
"lineNumber": -1,
"columnNumber": -1
},
"children": [2, 71],
"hitCount": 0,
"id": 1
}
],
"startTime": 194737272346,
"endTime": 194737292265,
"samples": [1, 1, 1],
"timeDeltas": [7489, 1185, 1271]
}


From the docs: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile



nodes array ProfileNode - The list of profile nodes. First item is the root node.
startTime number - Profiling start timestamp in microseconds.
endTime number - Profiling end timestamp in microseconds.
samples array [ integer ] - Ids of samples top nodes.
timeDeltas array [ integer ] - Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime.



There are multiple libraries like d3-flame-graph which allow to render all nodes as a flame graph:



FlameGraph Example



However loading the same json file using the Google Chrome DevTools allows also to see the execution time and even the pauses between different calls:



ChromeDevTool Profile Example



Is there a way to render render a similar cpu profile chart like the Google Chrome Dev Tools?










share|improve this question





























    0















    Google Chrome and also the NodeJs inspector allow to generate a cpu profile with the following JSON structure:



    excerpt



    {
    "nodes": [
    {
    "callFrame": {
    "functionName": "(root)",
    "scriptId": "0",
    "url": "",
    "lineNumber": -1,
    "columnNumber": -1
    },
    "children": [2, 71],
    "hitCount": 0,
    "id": 1
    }
    ],
    "startTime": 194737272346,
    "endTime": 194737292265,
    "samples": [1, 1, 1],
    "timeDeltas": [7489, 1185, 1271]
    }


    From the docs: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile



    nodes array ProfileNode - The list of profile nodes. First item is the root node.
    startTime number - Profiling start timestamp in microseconds.
    endTime number - Profiling end timestamp in microseconds.
    samples array [ integer ] - Ids of samples top nodes.
    timeDeltas array [ integer ] - Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime.



    There are multiple libraries like d3-flame-graph which allow to render all nodes as a flame graph:



    FlameGraph Example



    However loading the same json file using the Google Chrome DevTools allows also to see the execution time and even the pauses between different calls:



    ChromeDevTool Profile Example



    Is there a way to render render a similar cpu profile chart like the Google Chrome Dev Tools?










    share|improve this question



























      0












      0








      0








      Google Chrome and also the NodeJs inspector allow to generate a cpu profile with the following JSON structure:



      excerpt



      {
      "nodes": [
      {
      "callFrame": {
      "functionName": "(root)",
      "scriptId": "0",
      "url": "",
      "lineNumber": -1,
      "columnNumber": -1
      },
      "children": [2, 71],
      "hitCount": 0,
      "id": 1
      }
      ],
      "startTime": 194737272346,
      "endTime": 194737292265,
      "samples": [1, 1, 1],
      "timeDeltas": [7489, 1185, 1271]
      }


      From the docs: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile



      nodes array ProfileNode - The list of profile nodes. First item is the root node.
      startTime number - Profiling start timestamp in microseconds.
      endTime number - Profiling end timestamp in microseconds.
      samples array [ integer ] - Ids of samples top nodes.
      timeDeltas array [ integer ] - Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime.



      There are multiple libraries like d3-flame-graph which allow to render all nodes as a flame graph:



      FlameGraph Example



      However loading the same json file using the Google Chrome DevTools allows also to see the execution time and even the pauses between different calls:



      ChromeDevTool Profile Example



      Is there a way to render render a similar cpu profile chart like the Google Chrome Dev Tools?










      share|improve this question
















      Google Chrome and also the NodeJs inspector allow to generate a cpu profile with the following JSON structure:



      excerpt



      {
      "nodes": [
      {
      "callFrame": {
      "functionName": "(root)",
      "scriptId": "0",
      "url": "",
      "lineNumber": -1,
      "columnNumber": -1
      },
      "children": [2, 71],
      "hitCount": 0,
      "id": 1
      }
      ],
      "startTime": 194737272346,
      "endTime": 194737292265,
      "samples": [1, 1, 1],
      "timeDeltas": [7489, 1185, 1271]
      }


      From the docs: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile



      nodes array ProfileNode - The list of profile nodes. First item is the root node.
      startTime number - Profiling start timestamp in microseconds.
      endTime number - Profiling end timestamp in microseconds.
      samples array [ integer ] - Ids of samples top nodes.
      timeDeltas array [ integer ] - Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime.



      There are multiple libraries like d3-flame-graph which allow to render all nodes as a flame graph:



      FlameGraph Example



      However loading the same json file using the Google Chrome DevTools allows also to see the execution time and even the pauses between different calls:



      ChromeDevTool Profile Example



      Is there a way to render render a similar cpu profile chart like the Google Chrome Dev Tools?







      javascript google-chrome profiling cpu






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 2 at 9:45







      jantimon

















      asked Jan 1 at 23:02









      jantimonjantimon

      21.2k2196161




      21.2k2196161
























          1 Answer
          1






          active

          oldest

          votes


















          0














          For every profile id in samples there is also a microsecond measurement in timeDeltas.



          Combining the ids inside samples with the entries inside nodes allowed me to get all information needed.



          After that it is possible to add up all parents of the nodes and to calculate the execution time.



          In the end all equal parents are merged together for faster chart renderings.



          You can take a look at the code which is also released on github and npm:




          • https://github.com/jantimon/cpuprofile-to-flamegraph

          • https://www.npmjs.com/package/cpuprofile-to-flamegraph


          Code:



          /**
          * A parsed .cpuprofile which can be generated from
          * chrome or https://nodejs.org/api/inspector.html#inspector_cpu_profiler
          *
          * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile
          */
          export type Profile = {
          /**
          * The list of profile nodes. First item is the root node.
          */
          nodes: Array<ProfileNode>;
          /**
          * Profiling start timestamp in microseconds.
          */
          startTime: number;
          /**
          * Profiling end timestamp in microseconds.
          */
          endTime: number;
          /**
          * Ids of samples top nodes.
          */
          samples: Array<number>;
          /**
          * Time intervals between adjacent samples in microseconds.
          * The first delta is relative to the profile startTime.
          */
          timeDeltas: Array<number>;
          };

          /**
          * Profile node. Holds callsite information, execution statistics and child nodes.
          * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ProfileNode
          */
          export type ProfileNode = {
          /**
          * Unique id of the node.
          */
          id: number;
          /**
          * Runtime.CallFrame
          * Function location
          */
          callFrame: {
          /**
          * JavaScript function name.
          */
          functionName?: string;
          /**
          * JavaScript script id.
          */
          scriptId: string;
          /**
          * JavaScript script name or url.
          */
          url: string;
          /**
          * JavaScript script line number (0-based).
          */
          lineNumber: number;
          /**
          * JavaScript script column number (0-based).
          */
          columnNumber: number;
          };
          /**
          * Number of samples where this node was on top of the call stack.
          */
          hitCount?: number;
          /**
          * Child node ids.
          */
          children?: number;
          };

          /**
          * D3-FlameGraph input format
          * https://github.com/spiermar/d3-flame-graph#input-format
          */
          export type FlameGraphNode = {
          /**
          * JavaScript function name.
          */
          name: string;
          /**
          * Self execution time
          */
          value: number;
          /**
          * Execution time including child nodes
          */
          executionTime: number;
          /**
          * Child nodes
          */
          children: Array<FlameGraphNode>;
          /**
          * Original profiler node
          */
          profileNode: ProfileNode;
          /**
          * nodeModule name if known
          */
          nodeModule?: string;
          /**
          * Parent node
          */
          parent?: FlameGraphNode;
          };

          /**
          * Convert a cpuprofile into a FlameGraph
          */
          export function convertToMergedFlameGraph(cpuProfile: Profile): FlameGraphNode {
          const nodes = convertToTimedFlameGraph(cpuProfile);
          // Add all parent nodes
          const parentNodes = nodes.map(node => {
          const executionTime = node.value;
          node = Object.assign({}, node, { children: , executionTime });
          while (node.parent && node.parent.children) {
          const newParent = Object.assign({}, node.parent, {
          children: [node],
          executionTime
          });
          node.parent = newParent;
          node = newParent;
          }
          return node;
          });
          const mergedNodes: Array<FlameGraphNode> = ;
          let currentNode = parentNodes[0];
          // Merge equal parent nodes
          for (let nodeIndex = 1; nodeIndex <= parentNodes.length; nodeIndex++) {
          const nextNode = parentNodes[nodeIndex];
          const isMergeAble =
          nextNode !== undefined &&
          currentNode.profileNode === nextNode.profileNode &&
          currentNode.children.length &&
          nextNode.children.length;
          if (!isMergeAble) {
          mergedNodes.push(currentNode);
          currentNode = nextNode;
          } else {
          // Find common child
          let currentMergeNode = currentNode;
          let nextMergeNode = nextNode;
          while (true) {
          // Child nodes are sorted in chronological order
          // as nextNode is executed after currentNode it
          // is only possible to merge into the last child
          const lastChildIndex = currentMergeNode.children.length - 1;
          const mergeCandidate1 =
          currentMergeNode.children[lastChildIndex];
          const mergeCandidate2 = nextMergeNode.children[0];
          // As `getReducedSamples` already reduced all children
          // only nodes with children are possible merge targets
          const nodesHaveChildren =
          mergeCandidate1.children.length &&
          mergeCandidate2.children.length;
          if (
          nodesHaveChildren &&
          mergeCandidate1.profileNode.id ===
          mergeCandidate2.profileNode.id
          ) {
          currentMergeNode = mergeCandidate1;
          nextMergeNode = mergeCandidate2;
          } else {
          break;
          }
          }
          // Merge the last mergeable node
          currentMergeNode.children.push(nextMergeNode.children[0]);
          nextMergeNode.children[0].parent = currentMergeNode;
          const additionalExecutionTime = nextMergeNode.executionTime;
          let currentExecutionTimeNode:
          | FlameGraphNode
          | undefined = currentMergeNode;
          while (currentExecutionTimeNode) {
          currentExecutionTimeNode.executionTime += additionalExecutionTime;
          currentExecutionTimeNode = currentExecutionTimeNode.parent;
          }
          }
          }
          return mergedNodes[0];
          }

          function convertToTimedFlameGraph(cpuProfile: Profile): Array<FlameGraphNode> {
          // Convert into FrameGraphNodes structure
          const linkedNodes: Array<FlameGraphNode> = cpuProfile.nodes.map(
          (node: ProfileNode) => ({
          name: node.callFrame.functionName || "(anonymous function)",
          value: 0,
          executionTime: 0,
          children: ,
          profileNode: node,
          nodeModule: node.callFrame.url
          ? getNodeModuleName(node.callFrame.url)
          : undefined
          })
          );
          // Create a map for id lookups
          const flameGraphNodeById = new Map<number, FlameGraphNode>();
          cpuProfile.nodes.forEach((node, i) => {
          flameGraphNodeById.set(node.id, linkedNodes[i]);
          });
          // Create reference to children
          linkedNodes.forEach(linkedNode => {
          const children = linkedNode.profileNode.children || ;
          linkedNode.children = children.map(
          childNodeId => flameGraphNodeById.get(childNodeId) as FlameGraphNode
          );
          linkedNode.children.forEach(child => {
          child.parent = linkedNode;
          });
          });

          const { reducedSamples, reducedTimeDeltas } = getReducedSamples(cpuProfile);
          const timedRootNodes = reducedSamples.map((sampleId, i) =>
          Object.assign({}, flameGraphNodeById.get(sampleId), {
          value: reducedTimeDeltas[i]
          })
          );

          return timedRootNodes;
          }

          /**
          * If multiple samples in a row are the same they can be
          * combined
          *
          * This function returns a merged version of a cpuProfiles
          * samples and timeDeltas
          */
          function getReducedSamples({
          samples,
          timeDeltas
          }: {
          samples: Array<number>;
          timeDeltas: Array<number>;
          }): { reducedSamples: Array<number>; reducedTimeDeltas: Array<number> } {
          const sampleCount = samples.length;
          const reducedSamples: Array<number> = ;
          const reducedTimeDeltas: Array<number> = ;
          if (sampleCount === 0) {
          return { reducedSamples, reducedTimeDeltas };
          }
          let reducedSampleId = samples[0];
          let reducedTimeDelta = timeDeltas[0];
          for (let i = 0; i <= sampleCount; i++) {
          if (reducedSampleId === samples[i]) {
          reducedTimeDelta += timeDeltas[i];
          } else {
          reducedSamples.push(reducedSampleId);
          reducedTimeDeltas.push(reducedTimeDelta);
          reducedSampleId = samples[i];
          reducedTimeDelta = timeDeltas[i];
          }
          }
          return { reducedSamples, reducedTimeDeltas };
          }

          /**
          * Extract the node_modules name from a url
          */
          function getNodeModuleName(url: string): string | undefined {
          const nodeModules = "/node_modules/";
          const nodeModulesPosition = url.lastIndexOf(nodeModules);
          if (nodeModulesPosition === -1) {
          return undefined;
          }
          const folderNamePosition = url.indexOf("/", nodeModulesPosition + 1);
          const folderNamePositionEnd = url.indexOf("/", folderNamePosition + 1);
          if (folderNamePosition === -1 || folderNamePositionEnd === -1) {
          return undefined;
          }
          return url.substr(
          folderNamePosition + 1,
          folderNamePositionEnd - folderNamePosition - 1
          );
          }





          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999609%2funderstanding-javascript-cpu-profiles%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            For every profile id in samples there is also a microsecond measurement in timeDeltas.



            Combining the ids inside samples with the entries inside nodes allowed me to get all information needed.



            After that it is possible to add up all parents of the nodes and to calculate the execution time.



            In the end all equal parents are merged together for faster chart renderings.



            You can take a look at the code which is also released on github and npm:




            • https://github.com/jantimon/cpuprofile-to-flamegraph

            • https://www.npmjs.com/package/cpuprofile-to-flamegraph


            Code:



            /**
            * A parsed .cpuprofile which can be generated from
            * chrome or https://nodejs.org/api/inspector.html#inspector_cpu_profiler
            *
            * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile
            */
            export type Profile = {
            /**
            * The list of profile nodes. First item is the root node.
            */
            nodes: Array<ProfileNode>;
            /**
            * Profiling start timestamp in microseconds.
            */
            startTime: number;
            /**
            * Profiling end timestamp in microseconds.
            */
            endTime: number;
            /**
            * Ids of samples top nodes.
            */
            samples: Array<number>;
            /**
            * Time intervals between adjacent samples in microseconds.
            * The first delta is relative to the profile startTime.
            */
            timeDeltas: Array<number>;
            };

            /**
            * Profile node. Holds callsite information, execution statistics and child nodes.
            * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ProfileNode
            */
            export type ProfileNode = {
            /**
            * Unique id of the node.
            */
            id: number;
            /**
            * Runtime.CallFrame
            * Function location
            */
            callFrame: {
            /**
            * JavaScript function name.
            */
            functionName?: string;
            /**
            * JavaScript script id.
            */
            scriptId: string;
            /**
            * JavaScript script name or url.
            */
            url: string;
            /**
            * JavaScript script line number (0-based).
            */
            lineNumber: number;
            /**
            * JavaScript script column number (0-based).
            */
            columnNumber: number;
            };
            /**
            * Number of samples where this node was on top of the call stack.
            */
            hitCount?: number;
            /**
            * Child node ids.
            */
            children?: number;
            };

            /**
            * D3-FlameGraph input format
            * https://github.com/spiermar/d3-flame-graph#input-format
            */
            export type FlameGraphNode = {
            /**
            * JavaScript function name.
            */
            name: string;
            /**
            * Self execution time
            */
            value: number;
            /**
            * Execution time including child nodes
            */
            executionTime: number;
            /**
            * Child nodes
            */
            children: Array<FlameGraphNode>;
            /**
            * Original profiler node
            */
            profileNode: ProfileNode;
            /**
            * nodeModule name if known
            */
            nodeModule?: string;
            /**
            * Parent node
            */
            parent?: FlameGraphNode;
            };

            /**
            * Convert a cpuprofile into a FlameGraph
            */
            export function convertToMergedFlameGraph(cpuProfile: Profile): FlameGraphNode {
            const nodes = convertToTimedFlameGraph(cpuProfile);
            // Add all parent nodes
            const parentNodes = nodes.map(node => {
            const executionTime = node.value;
            node = Object.assign({}, node, { children: , executionTime });
            while (node.parent && node.parent.children) {
            const newParent = Object.assign({}, node.parent, {
            children: [node],
            executionTime
            });
            node.parent = newParent;
            node = newParent;
            }
            return node;
            });
            const mergedNodes: Array<FlameGraphNode> = ;
            let currentNode = parentNodes[0];
            // Merge equal parent nodes
            for (let nodeIndex = 1; nodeIndex <= parentNodes.length; nodeIndex++) {
            const nextNode = parentNodes[nodeIndex];
            const isMergeAble =
            nextNode !== undefined &&
            currentNode.profileNode === nextNode.profileNode &&
            currentNode.children.length &&
            nextNode.children.length;
            if (!isMergeAble) {
            mergedNodes.push(currentNode);
            currentNode = nextNode;
            } else {
            // Find common child
            let currentMergeNode = currentNode;
            let nextMergeNode = nextNode;
            while (true) {
            // Child nodes are sorted in chronological order
            // as nextNode is executed after currentNode it
            // is only possible to merge into the last child
            const lastChildIndex = currentMergeNode.children.length - 1;
            const mergeCandidate1 =
            currentMergeNode.children[lastChildIndex];
            const mergeCandidate2 = nextMergeNode.children[0];
            // As `getReducedSamples` already reduced all children
            // only nodes with children are possible merge targets
            const nodesHaveChildren =
            mergeCandidate1.children.length &&
            mergeCandidate2.children.length;
            if (
            nodesHaveChildren &&
            mergeCandidate1.profileNode.id ===
            mergeCandidate2.profileNode.id
            ) {
            currentMergeNode = mergeCandidate1;
            nextMergeNode = mergeCandidate2;
            } else {
            break;
            }
            }
            // Merge the last mergeable node
            currentMergeNode.children.push(nextMergeNode.children[0]);
            nextMergeNode.children[0].parent = currentMergeNode;
            const additionalExecutionTime = nextMergeNode.executionTime;
            let currentExecutionTimeNode:
            | FlameGraphNode
            | undefined = currentMergeNode;
            while (currentExecutionTimeNode) {
            currentExecutionTimeNode.executionTime += additionalExecutionTime;
            currentExecutionTimeNode = currentExecutionTimeNode.parent;
            }
            }
            }
            return mergedNodes[0];
            }

            function convertToTimedFlameGraph(cpuProfile: Profile): Array<FlameGraphNode> {
            // Convert into FrameGraphNodes structure
            const linkedNodes: Array<FlameGraphNode> = cpuProfile.nodes.map(
            (node: ProfileNode) => ({
            name: node.callFrame.functionName || "(anonymous function)",
            value: 0,
            executionTime: 0,
            children: ,
            profileNode: node,
            nodeModule: node.callFrame.url
            ? getNodeModuleName(node.callFrame.url)
            : undefined
            })
            );
            // Create a map for id lookups
            const flameGraphNodeById = new Map<number, FlameGraphNode>();
            cpuProfile.nodes.forEach((node, i) => {
            flameGraphNodeById.set(node.id, linkedNodes[i]);
            });
            // Create reference to children
            linkedNodes.forEach(linkedNode => {
            const children = linkedNode.profileNode.children || ;
            linkedNode.children = children.map(
            childNodeId => flameGraphNodeById.get(childNodeId) as FlameGraphNode
            );
            linkedNode.children.forEach(child => {
            child.parent = linkedNode;
            });
            });

            const { reducedSamples, reducedTimeDeltas } = getReducedSamples(cpuProfile);
            const timedRootNodes = reducedSamples.map((sampleId, i) =>
            Object.assign({}, flameGraphNodeById.get(sampleId), {
            value: reducedTimeDeltas[i]
            })
            );

            return timedRootNodes;
            }

            /**
            * If multiple samples in a row are the same they can be
            * combined
            *
            * This function returns a merged version of a cpuProfiles
            * samples and timeDeltas
            */
            function getReducedSamples({
            samples,
            timeDeltas
            }: {
            samples: Array<number>;
            timeDeltas: Array<number>;
            }): { reducedSamples: Array<number>; reducedTimeDeltas: Array<number> } {
            const sampleCount = samples.length;
            const reducedSamples: Array<number> = ;
            const reducedTimeDeltas: Array<number> = ;
            if (sampleCount === 0) {
            return { reducedSamples, reducedTimeDeltas };
            }
            let reducedSampleId = samples[0];
            let reducedTimeDelta = timeDeltas[0];
            for (let i = 0; i <= sampleCount; i++) {
            if (reducedSampleId === samples[i]) {
            reducedTimeDelta += timeDeltas[i];
            } else {
            reducedSamples.push(reducedSampleId);
            reducedTimeDeltas.push(reducedTimeDelta);
            reducedSampleId = samples[i];
            reducedTimeDelta = timeDeltas[i];
            }
            }
            return { reducedSamples, reducedTimeDeltas };
            }

            /**
            * Extract the node_modules name from a url
            */
            function getNodeModuleName(url: string): string | undefined {
            const nodeModules = "/node_modules/";
            const nodeModulesPosition = url.lastIndexOf(nodeModules);
            if (nodeModulesPosition === -1) {
            return undefined;
            }
            const folderNamePosition = url.indexOf("/", nodeModulesPosition + 1);
            const folderNamePositionEnd = url.indexOf("/", folderNamePosition + 1);
            if (folderNamePosition === -1 || folderNamePositionEnd === -1) {
            return undefined;
            }
            return url.substr(
            folderNamePosition + 1,
            folderNamePositionEnd - folderNamePosition - 1
            );
            }





            share|improve this answer




























              0














              For every profile id in samples there is also a microsecond measurement in timeDeltas.



              Combining the ids inside samples with the entries inside nodes allowed me to get all information needed.



              After that it is possible to add up all parents of the nodes and to calculate the execution time.



              In the end all equal parents are merged together for faster chart renderings.



              You can take a look at the code which is also released on github and npm:




              • https://github.com/jantimon/cpuprofile-to-flamegraph

              • https://www.npmjs.com/package/cpuprofile-to-flamegraph


              Code:



              /**
              * A parsed .cpuprofile which can be generated from
              * chrome or https://nodejs.org/api/inspector.html#inspector_cpu_profiler
              *
              * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile
              */
              export type Profile = {
              /**
              * The list of profile nodes. First item is the root node.
              */
              nodes: Array<ProfileNode>;
              /**
              * Profiling start timestamp in microseconds.
              */
              startTime: number;
              /**
              * Profiling end timestamp in microseconds.
              */
              endTime: number;
              /**
              * Ids of samples top nodes.
              */
              samples: Array<number>;
              /**
              * Time intervals between adjacent samples in microseconds.
              * The first delta is relative to the profile startTime.
              */
              timeDeltas: Array<number>;
              };

              /**
              * Profile node. Holds callsite information, execution statistics and child nodes.
              * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ProfileNode
              */
              export type ProfileNode = {
              /**
              * Unique id of the node.
              */
              id: number;
              /**
              * Runtime.CallFrame
              * Function location
              */
              callFrame: {
              /**
              * JavaScript function name.
              */
              functionName?: string;
              /**
              * JavaScript script id.
              */
              scriptId: string;
              /**
              * JavaScript script name or url.
              */
              url: string;
              /**
              * JavaScript script line number (0-based).
              */
              lineNumber: number;
              /**
              * JavaScript script column number (0-based).
              */
              columnNumber: number;
              };
              /**
              * Number of samples where this node was on top of the call stack.
              */
              hitCount?: number;
              /**
              * Child node ids.
              */
              children?: number;
              };

              /**
              * D3-FlameGraph input format
              * https://github.com/spiermar/d3-flame-graph#input-format
              */
              export type FlameGraphNode = {
              /**
              * JavaScript function name.
              */
              name: string;
              /**
              * Self execution time
              */
              value: number;
              /**
              * Execution time including child nodes
              */
              executionTime: number;
              /**
              * Child nodes
              */
              children: Array<FlameGraphNode>;
              /**
              * Original profiler node
              */
              profileNode: ProfileNode;
              /**
              * nodeModule name if known
              */
              nodeModule?: string;
              /**
              * Parent node
              */
              parent?: FlameGraphNode;
              };

              /**
              * Convert a cpuprofile into a FlameGraph
              */
              export function convertToMergedFlameGraph(cpuProfile: Profile): FlameGraphNode {
              const nodes = convertToTimedFlameGraph(cpuProfile);
              // Add all parent nodes
              const parentNodes = nodes.map(node => {
              const executionTime = node.value;
              node = Object.assign({}, node, { children: , executionTime });
              while (node.parent && node.parent.children) {
              const newParent = Object.assign({}, node.parent, {
              children: [node],
              executionTime
              });
              node.parent = newParent;
              node = newParent;
              }
              return node;
              });
              const mergedNodes: Array<FlameGraphNode> = ;
              let currentNode = parentNodes[0];
              // Merge equal parent nodes
              for (let nodeIndex = 1; nodeIndex <= parentNodes.length; nodeIndex++) {
              const nextNode = parentNodes[nodeIndex];
              const isMergeAble =
              nextNode !== undefined &&
              currentNode.profileNode === nextNode.profileNode &&
              currentNode.children.length &&
              nextNode.children.length;
              if (!isMergeAble) {
              mergedNodes.push(currentNode);
              currentNode = nextNode;
              } else {
              // Find common child
              let currentMergeNode = currentNode;
              let nextMergeNode = nextNode;
              while (true) {
              // Child nodes are sorted in chronological order
              // as nextNode is executed after currentNode it
              // is only possible to merge into the last child
              const lastChildIndex = currentMergeNode.children.length - 1;
              const mergeCandidate1 =
              currentMergeNode.children[lastChildIndex];
              const mergeCandidate2 = nextMergeNode.children[0];
              // As `getReducedSamples` already reduced all children
              // only nodes with children are possible merge targets
              const nodesHaveChildren =
              mergeCandidate1.children.length &&
              mergeCandidate2.children.length;
              if (
              nodesHaveChildren &&
              mergeCandidate1.profileNode.id ===
              mergeCandidate2.profileNode.id
              ) {
              currentMergeNode = mergeCandidate1;
              nextMergeNode = mergeCandidate2;
              } else {
              break;
              }
              }
              // Merge the last mergeable node
              currentMergeNode.children.push(nextMergeNode.children[0]);
              nextMergeNode.children[0].parent = currentMergeNode;
              const additionalExecutionTime = nextMergeNode.executionTime;
              let currentExecutionTimeNode:
              | FlameGraphNode
              | undefined = currentMergeNode;
              while (currentExecutionTimeNode) {
              currentExecutionTimeNode.executionTime += additionalExecutionTime;
              currentExecutionTimeNode = currentExecutionTimeNode.parent;
              }
              }
              }
              return mergedNodes[0];
              }

              function convertToTimedFlameGraph(cpuProfile: Profile): Array<FlameGraphNode> {
              // Convert into FrameGraphNodes structure
              const linkedNodes: Array<FlameGraphNode> = cpuProfile.nodes.map(
              (node: ProfileNode) => ({
              name: node.callFrame.functionName || "(anonymous function)",
              value: 0,
              executionTime: 0,
              children: ,
              profileNode: node,
              nodeModule: node.callFrame.url
              ? getNodeModuleName(node.callFrame.url)
              : undefined
              })
              );
              // Create a map for id lookups
              const flameGraphNodeById = new Map<number, FlameGraphNode>();
              cpuProfile.nodes.forEach((node, i) => {
              flameGraphNodeById.set(node.id, linkedNodes[i]);
              });
              // Create reference to children
              linkedNodes.forEach(linkedNode => {
              const children = linkedNode.profileNode.children || ;
              linkedNode.children = children.map(
              childNodeId => flameGraphNodeById.get(childNodeId) as FlameGraphNode
              );
              linkedNode.children.forEach(child => {
              child.parent = linkedNode;
              });
              });

              const { reducedSamples, reducedTimeDeltas } = getReducedSamples(cpuProfile);
              const timedRootNodes = reducedSamples.map((sampleId, i) =>
              Object.assign({}, flameGraphNodeById.get(sampleId), {
              value: reducedTimeDeltas[i]
              })
              );

              return timedRootNodes;
              }

              /**
              * If multiple samples in a row are the same they can be
              * combined
              *
              * This function returns a merged version of a cpuProfiles
              * samples and timeDeltas
              */
              function getReducedSamples({
              samples,
              timeDeltas
              }: {
              samples: Array<number>;
              timeDeltas: Array<number>;
              }): { reducedSamples: Array<number>; reducedTimeDeltas: Array<number> } {
              const sampleCount = samples.length;
              const reducedSamples: Array<number> = ;
              const reducedTimeDeltas: Array<number> = ;
              if (sampleCount === 0) {
              return { reducedSamples, reducedTimeDeltas };
              }
              let reducedSampleId = samples[0];
              let reducedTimeDelta = timeDeltas[0];
              for (let i = 0; i <= sampleCount; i++) {
              if (reducedSampleId === samples[i]) {
              reducedTimeDelta += timeDeltas[i];
              } else {
              reducedSamples.push(reducedSampleId);
              reducedTimeDeltas.push(reducedTimeDelta);
              reducedSampleId = samples[i];
              reducedTimeDelta = timeDeltas[i];
              }
              }
              return { reducedSamples, reducedTimeDeltas };
              }

              /**
              * Extract the node_modules name from a url
              */
              function getNodeModuleName(url: string): string | undefined {
              const nodeModules = "/node_modules/";
              const nodeModulesPosition = url.lastIndexOf(nodeModules);
              if (nodeModulesPosition === -1) {
              return undefined;
              }
              const folderNamePosition = url.indexOf("/", nodeModulesPosition + 1);
              const folderNamePositionEnd = url.indexOf("/", folderNamePosition + 1);
              if (folderNamePosition === -1 || folderNamePositionEnd === -1) {
              return undefined;
              }
              return url.substr(
              folderNamePosition + 1,
              folderNamePositionEnd - folderNamePosition - 1
              );
              }





              share|improve this answer


























                0












                0








                0







                For every profile id in samples there is also a microsecond measurement in timeDeltas.



                Combining the ids inside samples with the entries inside nodes allowed me to get all information needed.



                After that it is possible to add up all parents of the nodes and to calculate the execution time.



                In the end all equal parents are merged together for faster chart renderings.



                You can take a look at the code which is also released on github and npm:




                • https://github.com/jantimon/cpuprofile-to-flamegraph

                • https://www.npmjs.com/package/cpuprofile-to-flamegraph


                Code:



                /**
                * A parsed .cpuprofile which can be generated from
                * chrome or https://nodejs.org/api/inspector.html#inspector_cpu_profiler
                *
                * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile
                */
                export type Profile = {
                /**
                * The list of profile nodes. First item is the root node.
                */
                nodes: Array<ProfileNode>;
                /**
                * Profiling start timestamp in microseconds.
                */
                startTime: number;
                /**
                * Profiling end timestamp in microseconds.
                */
                endTime: number;
                /**
                * Ids of samples top nodes.
                */
                samples: Array<number>;
                /**
                * Time intervals between adjacent samples in microseconds.
                * The first delta is relative to the profile startTime.
                */
                timeDeltas: Array<number>;
                };

                /**
                * Profile node. Holds callsite information, execution statistics and child nodes.
                * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ProfileNode
                */
                export type ProfileNode = {
                /**
                * Unique id of the node.
                */
                id: number;
                /**
                * Runtime.CallFrame
                * Function location
                */
                callFrame: {
                /**
                * JavaScript function name.
                */
                functionName?: string;
                /**
                * JavaScript script id.
                */
                scriptId: string;
                /**
                * JavaScript script name or url.
                */
                url: string;
                /**
                * JavaScript script line number (0-based).
                */
                lineNumber: number;
                /**
                * JavaScript script column number (0-based).
                */
                columnNumber: number;
                };
                /**
                * Number of samples where this node was on top of the call stack.
                */
                hitCount?: number;
                /**
                * Child node ids.
                */
                children?: number;
                };

                /**
                * D3-FlameGraph input format
                * https://github.com/spiermar/d3-flame-graph#input-format
                */
                export type FlameGraphNode = {
                /**
                * JavaScript function name.
                */
                name: string;
                /**
                * Self execution time
                */
                value: number;
                /**
                * Execution time including child nodes
                */
                executionTime: number;
                /**
                * Child nodes
                */
                children: Array<FlameGraphNode>;
                /**
                * Original profiler node
                */
                profileNode: ProfileNode;
                /**
                * nodeModule name if known
                */
                nodeModule?: string;
                /**
                * Parent node
                */
                parent?: FlameGraphNode;
                };

                /**
                * Convert a cpuprofile into a FlameGraph
                */
                export function convertToMergedFlameGraph(cpuProfile: Profile): FlameGraphNode {
                const nodes = convertToTimedFlameGraph(cpuProfile);
                // Add all parent nodes
                const parentNodes = nodes.map(node => {
                const executionTime = node.value;
                node = Object.assign({}, node, { children: , executionTime });
                while (node.parent && node.parent.children) {
                const newParent = Object.assign({}, node.parent, {
                children: [node],
                executionTime
                });
                node.parent = newParent;
                node = newParent;
                }
                return node;
                });
                const mergedNodes: Array<FlameGraphNode> = ;
                let currentNode = parentNodes[0];
                // Merge equal parent nodes
                for (let nodeIndex = 1; nodeIndex <= parentNodes.length; nodeIndex++) {
                const nextNode = parentNodes[nodeIndex];
                const isMergeAble =
                nextNode !== undefined &&
                currentNode.profileNode === nextNode.profileNode &&
                currentNode.children.length &&
                nextNode.children.length;
                if (!isMergeAble) {
                mergedNodes.push(currentNode);
                currentNode = nextNode;
                } else {
                // Find common child
                let currentMergeNode = currentNode;
                let nextMergeNode = nextNode;
                while (true) {
                // Child nodes are sorted in chronological order
                // as nextNode is executed after currentNode it
                // is only possible to merge into the last child
                const lastChildIndex = currentMergeNode.children.length - 1;
                const mergeCandidate1 =
                currentMergeNode.children[lastChildIndex];
                const mergeCandidate2 = nextMergeNode.children[0];
                // As `getReducedSamples` already reduced all children
                // only nodes with children are possible merge targets
                const nodesHaveChildren =
                mergeCandidate1.children.length &&
                mergeCandidate2.children.length;
                if (
                nodesHaveChildren &&
                mergeCandidate1.profileNode.id ===
                mergeCandidate2.profileNode.id
                ) {
                currentMergeNode = mergeCandidate1;
                nextMergeNode = mergeCandidate2;
                } else {
                break;
                }
                }
                // Merge the last mergeable node
                currentMergeNode.children.push(nextMergeNode.children[0]);
                nextMergeNode.children[0].parent = currentMergeNode;
                const additionalExecutionTime = nextMergeNode.executionTime;
                let currentExecutionTimeNode:
                | FlameGraphNode
                | undefined = currentMergeNode;
                while (currentExecutionTimeNode) {
                currentExecutionTimeNode.executionTime += additionalExecutionTime;
                currentExecutionTimeNode = currentExecutionTimeNode.parent;
                }
                }
                }
                return mergedNodes[0];
                }

                function convertToTimedFlameGraph(cpuProfile: Profile): Array<FlameGraphNode> {
                // Convert into FrameGraphNodes structure
                const linkedNodes: Array<FlameGraphNode> = cpuProfile.nodes.map(
                (node: ProfileNode) => ({
                name: node.callFrame.functionName || "(anonymous function)",
                value: 0,
                executionTime: 0,
                children: ,
                profileNode: node,
                nodeModule: node.callFrame.url
                ? getNodeModuleName(node.callFrame.url)
                : undefined
                })
                );
                // Create a map for id lookups
                const flameGraphNodeById = new Map<number, FlameGraphNode>();
                cpuProfile.nodes.forEach((node, i) => {
                flameGraphNodeById.set(node.id, linkedNodes[i]);
                });
                // Create reference to children
                linkedNodes.forEach(linkedNode => {
                const children = linkedNode.profileNode.children || ;
                linkedNode.children = children.map(
                childNodeId => flameGraphNodeById.get(childNodeId) as FlameGraphNode
                );
                linkedNode.children.forEach(child => {
                child.parent = linkedNode;
                });
                });

                const { reducedSamples, reducedTimeDeltas } = getReducedSamples(cpuProfile);
                const timedRootNodes = reducedSamples.map((sampleId, i) =>
                Object.assign({}, flameGraphNodeById.get(sampleId), {
                value: reducedTimeDeltas[i]
                })
                );

                return timedRootNodes;
                }

                /**
                * If multiple samples in a row are the same they can be
                * combined
                *
                * This function returns a merged version of a cpuProfiles
                * samples and timeDeltas
                */
                function getReducedSamples({
                samples,
                timeDeltas
                }: {
                samples: Array<number>;
                timeDeltas: Array<number>;
                }): { reducedSamples: Array<number>; reducedTimeDeltas: Array<number> } {
                const sampleCount = samples.length;
                const reducedSamples: Array<number> = ;
                const reducedTimeDeltas: Array<number> = ;
                if (sampleCount === 0) {
                return { reducedSamples, reducedTimeDeltas };
                }
                let reducedSampleId = samples[0];
                let reducedTimeDelta = timeDeltas[0];
                for (let i = 0; i <= sampleCount; i++) {
                if (reducedSampleId === samples[i]) {
                reducedTimeDelta += timeDeltas[i];
                } else {
                reducedSamples.push(reducedSampleId);
                reducedTimeDeltas.push(reducedTimeDelta);
                reducedSampleId = samples[i];
                reducedTimeDelta = timeDeltas[i];
                }
                }
                return { reducedSamples, reducedTimeDeltas };
                }

                /**
                * Extract the node_modules name from a url
                */
                function getNodeModuleName(url: string): string | undefined {
                const nodeModules = "/node_modules/";
                const nodeModulesPosition = url.lastIndexOf(nodeModules);
                if (nodeModulesPosition === -1) {
                return undefined;
                }
                const folderNamePosition = url.indexOf("/", nodeModulesPosition + 1);
                const folderNamePositionEnd = url.indexOf("/", folderNamePosition + 1);
                if (folderNamePosition === -1 || folderNamePositionEnd === -1) {
                return undefined;
                }
                return url.substr(
                folderNamePosition + 1,
                folderNamePositionEnd - folderNamePosition - 1
                );
                }





                share|improve this answer













                For every profile id in samples there is also a microsecond measurement in timeDeltas.



                Combining the ids inside samples with the entries inside nodes allowed me to get all information needed.



                After that it is possible to add up all parents of the nodes and to calculate the execution time.



                In the end all equal parents are merged together for faster chart renderings.



                You can take a look at the code which is also released on github and npm:




                • https://github.com/jantimon/cpuprofile-to-flamegraph

                • https://www.npmjs.com/package/cpuprofile-to-flamegraph


                Code:



                /**
                * A parsed .cpuprofile which can be generated from
                * chrome or https://nodejs.org/api/inspector.html#inspector_cpu_profiler
                *
                * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-Profile
                */
                export type Profile = {
                /**
                * The list of profile nodes. First item is the root node.
                */
                nodes: Array<ProfileNode>;
                /**
                * Profiling start timestamp in microseconds.
                */
                startTime: number;
                /**
                * Profiling end timestamp in microseconds.
                */
                endTime: number;
                /**
                * Ids of samples top nodes.
                */
                samples: Array<number>;
                /**
                * Time intervals between adjacent samples in microseconds.
                * The first delta is relative to the profile startTime.
                */
                timeDeltas: Array<number>;
                };

                /**
                * Profile node. Holds callsite information, execution statistics and child nodes.
                * https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ProfileNode
                */
                export type ProfileNode = {
                /**
                * Unique id of the node.
                */
                id: number;
                /**
                * Runtime.CallFrame
                * Function location
                */
                callFrame: {
                /**
                * JavaScript function name.
                */
                functionName?: string;
                /**
                * JavaScript script id.
                */
                scriptId: string;
                /**
                * JavaScript script name or url.
                */
                url: string;
                /**
                * JavaScript script line number (0-based).
                */
                lineNumber: number;
                /**
                * JavaScript script column number (0-based).
                */
                columnNumber: number;
                };
                /**
                * Number of samples where this node was on top of the call stack.
                */
                hitCount?: number;
                /**
                * Child node ids.
                */
                children?: number;
                };

                /**
                * D3-FlameGraph input format
                * https://github.com/spiermar/d3-flame-graph#input-format
                */
                export type FlameGraphNode = {
                /**
                * JavaScript function name.
                */
                name: string;
                /**
                * Self execution time
                */
                value: number;
                /**
                * Execution time including child nodes
                */
                executionTime: number;
                /**
                * Child nodes
                */
                children: Array<FlameGraphNode>;
                /**
                * Original profiler node
                */
                profileNode: ProfileNode;
                /**
                * nodeModule name if known
                */
                nodeModule?: string;
                /**
                * Parent node
                */
                parent?: FlameGraphNode;
                };

                /**
                * Convert a cpuprofile into a FlameGraph
                */
                export function convertToMergedFlameGraph(cpuProfile: Profile): FlameGraphNode {
                const nodes = convertToTimedFlameGraph(cpuProfile);
                // Add all parent nodes
                const parentNodes = nodes.map(node => {
                const executionTime = node.value;
                node = Object.assign({}, node, { children: , executionTime });
                while (node.parent && node.parent.children) {
                const newParent = Object.assign({}, node.parent, {
                children: [node],
                executionTime
                });
                node.parent = newParent;
                node = newParent;
                }
                return node;
                });
                const mergedNodes: Array<FlameGraphNode> = ;
                let currentNode = parentNodes[0];
                // Merge equal parent nodes
                for (let nodeIndex = 1; nodeIndex <= parentNodes.length; nodeIndex++) {
                const nextNode = parentNodes[nodeIndex];
                const isMergeAble =
                nextNode !== undefined &&
                currentNode.profileNode === nextNode.profileNode &&
                currentNode.children.length &&
                nextNode.children.length;
                if (!isMergeAble) {
                mergedNodes.push(currentNode);
                currentNode = nextNode;
                } else {
                // Find common child
                let currentMergeNode = currentNode;
                let nextMergeNode = nextNode;
                while (true) {
                // Child nodes are sorted in chronological order
                // as nextNode is executed after currentNode it
                // is only possible to merge into the last child
                const lastChildIndex = currentMergeNode.children.length - 1;
                const mergeCandidate1 =
                currentMergeNode.children[lastChildIndex];
                const mergeCandidate2 = nextMergeNode.children[0];
                // As `getReducedSamples` already reduced all children
                // only nodes with children are possible merge targets
                const nodesHaveChildren =
                mergeCandidate1.children.length &&
                mergeCandidate2.children.length;
                if (
                nodesHaveChildren &&
                mergeCandidate1.profileNode.id ===
                mergeCandidate2.profileNode.id
                ) {
                currentMergeNode = mergeCandidate1;
                nextMergeNode = mergeCandidate2;
                } else {
                break;
                }
                }
                // Merge the last mergeable node
                currentMergeNode.children.push(nextMergeNode.children[0]);
                nextMergeNode.children[0].parent = currentMergeNode;
                const additionalExecutionTime = nextMergeNode.executionTime;
                let currentExecutionTimeNode:
                | FlameGraphNode
                | undefined = currentMergeNode;
                while (currentExecutionTimeNode) {
                currentExecutionTimeNode.executionTime += additionalExecutionTime;
                currentExecutionTimeNode = currentExecutionTimeNode.parent;
                }
                }
                }
                return mergedNodes[0];
                }

                function convertToTimedFlameGraph(cpuProfile: Profile): Array<FlameGraphNode> {
                // Convert into FrameGraphNodes structure
                const linkedNodes: Array<FlameGraphNode> = cpuProfile.nodes.map(
                (node: ProfileNode) => ({
                name: node.callFrame.functionName || "(anonymous function)",
                value: 0,
                executionTime: 0,
                children: ,
                profileNode: node,
                nodeModule: node.callFrame.url
                ? getNodeModuleName(node.callFrame.url)
                : undefined
                })
                );
                // Create a map for id lookups
                const flameGraphNodeById = new Map<number, FlameGraphNode>();
                cpuProfile.nodes.forEach((node, i) => {
                flameGraphNodeById.set(node.id, linkedNodes[i]);
                });
                // Create reference to children
                linkedNodes.forEach(linkedNode => {
                const children = linkedNode.profileNode.children || ;
                linkedNode.children = children.map(
                childNodeId => flameGraphNodeById.get(childNodeId) as FlameGraphNode
                );
                linkedNode.children.forEach(child => {
                child.parent = linkedNode;
                });
                });

                const { reducedSamples, reducedTimeDeltas } = getReducedSamples(cpuProfile);
                const timedRootNodes = reducedSamples.map((sampleId, i) =>
                Object.assign({}, flameGraphNodeById.get(sampleId), {
                value: reducedTimeDeltas[i]
                })
                );

                return timedRootNodes;
                }

                /**
                * If multiple samples in a row are the same they can be
                * combined
                *
                * This function returns a merged version of a cpuProfiles
                * samples and timeDeltas
                */
                function getReducedSamples({
                samples,
                timeDeltas
                }: {
                samples: Array<number>;
                timeDeltas: Array<number>;
                }): { reducedSamples: Array<number>; reducedTimeDeltas: Array<number> } {
                const sampleCount = samples.length;
                const reducedSamples: Array<number> = ;
                const reducedTimeDeltas: Array<number> = ;
                if (sampleCount === 0) {
                return { reducedSamples, reducedTimeDeltas };
                }
                let reducedSampleId = samples[0];
                let reducedTimeDelta = timeDeltas[0];
                for (let i = 0; i <= sampleCount; i++) {
                if (reducedSampleId === samples[i]) {
                reducedTimeDelta += timeDeltas[i];
                } else {
                reducedSamples.push(reducedSampleId);
                reducedTimeDeltas.push(reducedTimeDelta);
                reducedSampleId = samples[i];
                reducedTimeDelta = timeDeltas[i];
                }
                }
                return { reducedSamples, reducedTimeDeltas };
                }

                /**
                * Extract the node_modules name from a url
                */
                function getNodeModuleName(url: string): string | undefined {
                const nodeModules = "/node_modules/";
                const nodeModulesPosition = url.lastIndexOf(nodeModules);
                if (nodeModulesPosition === -1) {
                return undefined;
                }
                const folderNamePosition = url.indexOf("/", nodeModulesPosition + 1);
                const folderNamePositionEnd = url.indexOf("/", folderNamePosition + 1);
                if (folderNamePosition === -1 || folderNamePositionEnd === -1) {
                return undefined;
                }
                return url.substr(
                folderNamePosition + 1,
                folderNamePositionEnd - folderNamePosition - 1
                );
                }






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Jan 3 at 13:40









                jantimonjantimon

                21.2k2196161




                21.2k2196161
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999609%2funderstanding-javascript-cpu-profiles%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

                    Does disintegrating a polymorphed enemy still kill it after the 2018 errata?

                    A Topological Invariant for $pi_3(U(n))$