Skip to content

Commit 324392a

Browse files
committed
use cpu-massager package to get the same values as in the console
1 parent 44bda92 commit 324392a

File tree

6 files changed

+80
-78
lines changed

6 files changed

+80
-78
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/node_modules
2+
package-lock.json

index.js

+3-76
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,6 @@
11
const fs = require('fs')
22
const path = require('path')
3-
4-
function summarizeCPUProfile(profile, name) {
5-
// Parse the profile data
6-
const { nodes, samples, timeDeltas } = profile
7-
8-
const nodeMap = {}
9-
nodes.forEach(node => {
10-
nodeMap[node.id] = {
11-
id: node.id,
12-
functionName: node.callFrame.functionName || '(anonymous function)',
13-
selfTime: 0,
14-
totalTime: 0,
15-
hitCount: node.hitCount || 0,
16-
children: node.children || [],
17-
url: node.callFrame.url
18-
}
19-
})
20-
21-
// Calculate self time and total time for each node
22-
samples.forEach((sampleId, index) => {
23-
const deltaTime = timeDeltas[index] || 0
24-
if (nodeMap[sampleId]) {
25-
nodeMap[sampleId].selfTime += deltaTime
26-
}
27-
})
28-
29-
// Calculate total time for each node by traversing the tree
30-
function calculateTotalTime (nodeId) {
31-
const node = nodeMap[nodeId]
32-
if (!node) return 0
33-
node.totalTime = node.selfTime
34-
node.children.forEach(childId => {
35-
node.totalTime += calculateTotalTime(childId)
36-
})
37-
return node.totalTime
38-
}
39-
40-
nodes.forEach(node => {
41-
calculateTotalTime(node.id)
42-
})
43-
44-
// Identify top functions by self time and total time
45-
const topSelfTimeNodes = Object.values(nodeMap)
46-
.sort((a, b) => b.selfTime - a.selfTime)
47-
.slice(0, 10)
48-
49-
const topTotalTimeNodes = Object.values(nodeMap)
50-
.sort((a, b) => b.totalTime - a.totalTime)
51-
.slice(0, 10)
52-
53-
const highestTotalTime = topTotalTimeNodes[0].totalTime
54-
55-
// Generate summary
56-
function mapNodeData (node) {
57-
return {
58-
functionName: node.functionName,
59-
totalTime: node.totalTime / 1000,
60-
hitCount: node.hitCount,
61-
selfTime: node.selfTime / 1000,
62-
url: node.url,
63-
selfTimePercentage: ((node.selfTime / highestTotalTime) * 100).toFixed(2) + '%',
64-
totalTimePercentage: ((node.totalTime / highestTotalTime) * 100).toFixed(2) + '%'
65-
}
66-
}
67-
68-
const summary = {
69-
topSelfTimeFunctions: topSelfTimeNodes.map(mapNodeData),
70-
topTotalTimeFunctions: topTotalTimeNodes.map(mapNodeData),
71-
startTime: profile.startTime / 1000,
72-
endTime: profile.endTime / 1000
73-
}
74-
75-
return `{"messages":[{"role":"user","content":"Given the following CPU profile data: "${JSON.stringify(summary)}" answer the following question: <question>"},{"role":"assistant","content":"<Here goes the AI response>"}]}`
76-
}
3+
const { summarizeCPUProfile } = require('./utils/summarizeCPUProfile')
774

785
const args = process.argv.slice(2);
796

@@ -89,14 +16,14 @@ const outputFilePath = path.join(outputDir, outputFileName);
8916

9017
fs.readFile(file, 'utf8', (err, data) => {
9118
if (err) {
92-
console.error('Error reading file:', err);
19+
console.error('Error reading file:', err, 'reading error');
9320
process.exit(1);
9421
}
9522
const profile = JSON.parse(data);
9623
const summary = summarizeCPUProfile(profile);
9724
fs.writeFile(outputFilePath, summary, 'utf8', (err) => {
9825
if (err) {
99-
console.error('Error writing summary file:', err);
26+
console.error('Error writing summary file:', err, 'writing error');
10027
process.exit(1);
10128
}
10229
console.log(`Summary written to ${outputFilePath}`);

package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@
77
"test": "echo \"Error: no test specified\" && exit 1"
88
},
99
"author": "",
10-
"license": "ISC"
10+
"license": "ISC",
11+
"dependencies": {
12+
"@ns-private/cpu-profile-massager": "^1.4.1",
13+
"lodash": "^4.17.21"
14+
}
1115
}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"messages":[{"role":"user","content":"Given the following CPU profile data: "{"topSelfTimeFunctions":[{"functionName":"(idle)","totalTime":32898.753,"hitCount":27685,"selfTime":32898.753,"url":"","selfTimePercentage":"99.55%","totalTimePercentage":"99.55%"},{"functionName":"startThread","totalTime":30.695,"hitCount":13,"selfTime":30.695,"url":"","selfTimePercentage":"0.09%","totalTimePercentage":"0.09%"},{"functionName":"(program)","totalTime":8.51,"hitCount":9,"selfTime":8.51,"url":"","selfTimePercentage":"0.03%","totalTimePercentage":"0.03%"},{"functionName":"Worker","totalTime":42.051,"hitCount":5,"selfTime":7.268,"url":"node:internal/worker","selfTimePercentage":"0.02%","totalTimePercentage":"0.13%"},{"functionName":"(garbage collector)","totalTime":3.238,"hitCount":3,"selfTime":3.238,"url":"","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"createFunctionContext","totalTime":3.221,"hitCount":3,"selfTime":3.221,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/handlebars/dist/cjs/handlebars/compiler/javascript-compiler.js","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"next","totalTime":3.219,"hitCount":3,"selfTime":3.219,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"normalizeDirectives","totalTime":3.217,"hitCount":3,"selfTime":3.217,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/helmet/dist/cjs/index.js","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"run","totalTime":3.209,"hitCount":3,"selfTime":3.209,"url":"","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"Worker","totalTime":3.52,"hitCount":2,"selfTime":2.436,"url":"","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"}],"topTotalTimeFunctions":[{"functionName":"(root)","totalTime":33045.836,"hitCount":0,"selfTime":0,"url":"","selfTimePercentage":"0.00%","totalTimePercentage":"100.00%"},{"functionName":"(idle)","totalTime":32898.753,"hitCount":27685,"selfTime":32898.753,"url":"","selfTimePercentage":"99.55%","totalTimePercentage":"99.55%"},{"functionName":"processTicksAndRejections","totalTime":75.146,"hitCount":1,"selfTime":1.09,"url":"node:internal/process/task_queues","selfTimePercentage":"0.00%","totalTimePercentage":"0.23%"},{"functionName":"runMicrotasks","totalTime":58.402,"hitCount":0,"selfTime":0,"url":"","selfTimePercentage":"0.00%","totalTimePercentage":"0.18%"},{"functionName":"handleResolve","totalTime":56.227,"hitCount":1,"selfTime":1.089,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/hooks.js","selfTimePercentage":"0.00%","totalTimePercentage":"0.17%"},{"functionName":"next","totalTime":55.138,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/hooks.js","selfTimePercentage":"0.00%","totalTimePercentage":"0.17%"},{"functionName":"runPreParsing","totalTime":43.337,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/route.js","selfTimePercentage":"0.00%","totalTimePercentage":"0.13%"},{"functionName":"handleRequest","totalTime":43.337,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/handleRequest.js","selfTimePercentage":"0.00%","totalTimePercentage":"0.13%"},{"functionName":"handler","totalTime":43.337,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/handleRequest.js","selfTimePercentage":"0.00%","totalTimePercentage":"0.13%"},{"functionName":"preValidationCallback","totalTime":43.337,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/handleRequest.js","selfTimePercentage":"0.00%","totalTimePercentage":"0.13%"}],"startTime":1403555.695,"endTime":1436605.012}" answer the following question: <question>"},{"role":"assistant","content":"<Here goes the AI response>"}]}
1+
{"messages":[{"role":"user","content":"Given the following CPU profile data: "{"topSelfTimeFunctions":[{"functionName":"(idle)","totalTime":32891.36,"hitCount":27685,"selfTime":32891.36,"url":"","selfTimePercentage":"99.57%","totalTimePercentage":"99.57%"},{"functionName":"startThread","totalTime":22.3,"hitCount":13,"selfTime":22.3,"url":"","selfTimePercentage":"0.07%","totalTimePercentage":"0.07%"},{"functionName":"Worker","totalTime":43.02,"hitCount":5,"selfTime":12.75,"url":"node:internal/worker","selfTimePercentage":"0.04%","totalTimePercentage":"0.13%"},{"functionName":"normalizeDirectives","totalTime":3.24,"hitCount":3,"selfTime":3.24,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/helmet/dist/cjs/index.js","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"emit","totalTime":3.24,"hitCount":2,"selfTime":3.24,"url":"node:events","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"run","totalTime":3.23,"hitCount":3,"selfTime":3.23,"url":"","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"next","totalTime":3.22,"hitCount":3,"selfTime":3.22,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"createFunctionContext","totalTime":3.22,"hitCount":3,"selfTime":3.22,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/handlebars/dist/cjs/handlebars/compiler/javascript-compiler.js","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"(garbage collector)","totalTime":3.2,"hitCount":3,"selfTime":3.2,"url":"","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"},{"functionName":"#ensureSearchParamsUpdated","totalTime":2.98,"hitCount":1,"selfTime":2.98,"url":"node:internal/url","selfTimePercentage":"0.01%","totalTimePercentage":"0.01%"}],"topTotalTimeFunctions":[{"functionName":"(root)","totalTime":33033.69,"hitCount":0,"selfTime":0,"url":"","selfTimePercentage":"0%","totalTimePercentage":"100%"},{"functionName":"(idle)","totalTime":32891.36,"hitCount":27685,"selfTime":32891.36,"url":"","selfTimePercentage":"99.57%","totalTimePercentage":"99.57%"},{"functionName":"processTicksAndRejections","totalTime":75.6,"hitCount":1,"selfTime":1.05,"url":"node:internal/process/task_queues","selfTimePercentage":"0%","totalTimePercentage":"0.23%"},{"functionName":"runMicrotasks","totalTime":58.19,"hitCount":0,"selfTime":0,"url":"","selfTimePercentage":"0%","totalTimePercentage":"0.18%"},{"functionName":"handleResolve","totalTime":56.04,"hitCount":1,"selfTime":1.07,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/hooks.js","selfTimePercentage":"0%","totalTimePercentage":"0.17%"},{"functionName":"next","totalTime":54.96,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/hooks.js","selfTimePercentage":"0%","totalTimePercentage":"0.17%"},{"functionName":"runPreParsing","totalTime":43.1,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/route.js","selfTimePercentage":"0%","totalTimePercentage":"0.13%"},{"functionName":"handleRequest","totalTime":43.1,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/handleRequest.js","selfTimePercentage":"0%","totalTimePercentage":"0.13%"},{"functionName":"handler","totalTime":43.1,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/handleRequest.js","selfTimePercentage":"0%","totalTimePercentage":"0.13%"},{"functionName":"preValidationCallback","totalTime":43.1,"hitCount":0,"selfTime":0,"url":"/home/cesardiaz/dev-learning/fastify/url-shortener/node_modules/fastify/lib/handleRequest.js","selfTimePercentage":"0%","totalTimePercentage":"0.13%"}],"startTime":1403555.695,"endTime":1436605.012}" answer the following question: <question>"},{"role":"assistant","content":"<Here goes the AI response>"}]}

utils/flattenTree.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
function flattenTree (root) {
2+
const uniqueValuesById = new Map()
3+
4+
function traverse (node) {
5+
if (!node || uniqueValuesById.has(node.id)) return
6+
uniqueValuesById.set(node.id, node)
7+
if (node.children) {
8+
node.children.forEach(child => traverse(child))
9+
}
10+
}
11+
12+
traverse(root)
13+
return Array.from(uniqueValuesById.values())
14+
}
15+
16+
module.exports = { flattenTree }

utils/summarizeCPUProfile.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const { filterData, transformToOld } = require('@ns-private/cpu-profile-massager')
2+
const { flattenTree } = require('./flattenTree')
3+
4+
const roundToTwoDecimals = (num) => Math.round(num * 100) / 100
5+
6+
function summarizeCPUProfile (profile) {
7+
// use the massager to transform the profile and get the same values as the ones in the ui
8+
const parsedAsset = transformToOld(profile)
9+
const filteredAsset = filterData(parsedAsset, {
10+
showNSolid: false,
11+
showProgram: false,
12+
showGarbage: true,
13+
showIdle: true
14+
})
15+
16+
// get all nodes in a single array
17+
const nodes = flattenTree(filteredAsset.head)
18+
19+
// Identify top functions by self time and total time
20+
const topSelfTimeNodes = Object.values(nodes)
21+
.sort((a, b) => b.selfTime - a.selfTime)
22+
.slice(0, 10)
23+
24+
const topTotalTimeNodes = Object.values(nodes)
25+
.sort((a, b) => b.totalTime - a.totalTime)
26+
.slice(0, 10)
27+
28+
const highestTotalTime = roundToTwoDecimals(topTotalTimeNodes[0].totalTime)
29+
30+
// Generate summary
31+
function mapNodeData(node) {
32+
return {
33+
functionName: node.functionName,
34+
totalTime: roundToTwoDecimals(node.totalTime),
35+
hitCount: node.hitCount,
36+
selfTime: roundToTwoDecimals(node.selfTime),
37+
url: node.url,
38+
selfTimePercentage: roundToTwoDecimals((node.selfTime / highestTotalTime) * 100) + '%',
39+
totalTimePercentage: roundToTwoDecimals((node.totalTime / highestTotalTime) * 100) + '%'
40+
}
41+
}
42+
43+
const summary = {
44+
topSelfTimeFunctions: topSelfTimeNodes.map(mapNodeData),
45+
topTotalTimeFunctions: topTotalTimeNodes.map(mapNodeData),
46+
startTime: profile.startTime / 1000,
47+
endTime: profile.endTime / 1000
48+
}
49+
// return JSON.stringify(summary, null, 2)
50+
return `{"messages":[{"role":"user","content":"Given the following CPU profile data: "${JSON.stringify(summary)}" answer the following question: <question>"},{"role":"assistant","content":"<Here goes the AI response>"}]}`
51+
}
52+
53+
module.exports = { summarizeCPUProfile }

0 commit comments

Comments
 (0)