-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from RaisinTen/add-web-support
Add web support
- Loading branch information
Showing
21 changed files
with
7,894 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
events.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Client-side use on Web Browser | ||
|
||
## Server | ||
|
||
The [`server.js`](server.js) Node.js program serves the files in the current directory and also serves the `perfetto` module from [`../../../index.mjs`](../../../index.mjs). | ||
|
||
It also exposes the following endpoints: | ||
|
||
- `/api/perftrace` (`POST`) - Reads the performance trace from the HTTP request body, writes it to a file and closes the server | ||
- `/api/data[0-7]` (`GET`) - Serves `{"text": "<text-to-display-by-the-client>"}` after an artificial delay | ||
- `/api/submit` (`GET`) - Serves `{"data": "<submission-button-text>"}` after an artificial delay | ||
|
||
## Client | ||
|
||
The `perftrace.mjs` module is `import`ed in (only works here because the server is designed to serve the file with this name): | ||
|
||
https://github.com/RaisinTen/perftrace/blob/fbb0d21f13329eee9912373482e7821f61441621/docs/examples/client-side-use-on-web-browser/index.mjs#L1 | ||
|
||
A new `TraceEvents` object is created in: | ||
|
||
https://github.com/RaisinTen/perftrace/blob/fbb0d21f13329eee9912373482e7821f61441621/docs/examples/client-side-use-on-web-browser/index.mjs#L4 | ||
|
||
The client displays a list of loader animations while fetching the data from the `/api/data[0-7]` endpoint in parallel and display those in: | ||
|
||
https://github.com/RaisinTen/perftrace/blob/fbb0d21f13329eee9912373482e7821f61441621/docs/examples/client-side-use-on-web-browser/index.mjs#L10-L24 | ||
|
||
The loads are measured using the Performance Timeline APIs: | ||
|
||
```js | ||
performance.mark("before"); | ||
// code to measure | ||
performance.measure("after", "before"); | ||
``` | ||
|
||
Once the data is fully loaded, a button is displayed, clicking which, sends the performance trace to the `/api/perftrace` endpoint which causes the server to write the performance trace to a file and shut down: | ||
|
||
https://github.com/RaisinTen/perftrace/blob/fbb0d21f13329eee9912373482e7821f61441621/docs/examples/client-side-use-on-web-browser/index.mjs#L47-L61 | ||
|
||
After running `node server.js`, opening <http://localhost:8080> in your browser and clicking the `Submit trace` button, the generated `events.json` file can be opened on <https://ui.perfetto.dev> for visualization: | ||
|
||
![](./perfetto.png) | ||
|
||
Here's a demo: | ||
|
||
![](./perftrace-web.gif) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<html> | ||
<head> | ||
<script type="module" src="index.mjs"></script> | ||
<link rel="stylesheet" href="styles.css"> | ||
</head> | ||
<body> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { TraceEvents } from "./perftrace.mjs"; | ||
|
||
performance.mark("begin tracing"); | ||
const traceEvents = new TraceEvents(); | ||
|
||
const listItems = []; | ||
let submissionDiv; | ||
const NUM_ELEMENTS = 8; | ||
|
||
function fetchDataId(id) { | ||
performance.mark(`before fetching data ${id}`); | ||
return fetch(`/api/data${id}`) | ||
.then((response) => { | ||
return response.json(); | ||
}) | ||
.then((data) => { | ||
listItems[id].innerHTML = ""; | ||
const p = document.createElement("p"); | ||
p.className = "data"; | ||
p.innerText = data.text; | ||
listItems[id].appendChild(p); | ||
performance.measure(`fetch data ${id}`, `before fetching data ${id}`); | ||
}); | ||
} | ||
|
||
async function fetchData() { | ||
const dataFetches = []; | ||
for (let i = 0; i < NUM_ELEMENTS; ++i) { | ||
dataFetches.push(fetchDataId(i)); | ||
} | ||
await Promise.all(dataFetches); | ||
} | ||
|
||
function showLoaders() { | ||
const ul = document.createElement("ul"); | ||
for (let i = 0; i < NUM_ELEMENTS; ++i) { | ||
const div = document.createElement("div"); | ||
div.className = "loader"; | ||
const li = document.createElement("li"); | ||
listItems.push(li); | ||
li.appendChild(div); | ||
ul.appendChild(li); | ||
} | ||
document.body.appendChild(ul); | ||
} | ||
|
||
function submitTrace() { | ||
submissionDiv.innerHTML = ""; | ||
const events = traceEvents.getEvents(); | ||
traceEvents.destroy(); | ||
|
||
// post events to /api/perftrace | ||
fetch("/api/perftrace", { | ||
method: "POST", | ||
headers: { | ||
"Accept": "application/json", | ||
"Content-Type": "application/json" | ||
}, | ||
body: JSON.stringify(events) | ||
}); | ||
} | ||
|
||
async function displayButton() { | ||
performance.mark("creating submit button"); | ||
const fetchData = await fetch("/api/submit"); | ||
const fetchJson = await fetchData.json(); | ||
const data = fetchJson.data; | ||
submissionDiv = document.createElement("div"); | ||
const button = document.createElement("button"); | ||
button.className = "submit"; | ||
button.textContent = data; | ||
button.type = "button"; | ||
button.onclick = submitTrace; | ||
submissionDiv.appendChild(button); | ||
document.body.appendChild(submissionDiv); | ||
performance.measure("create submit button", "creating submit button"); | ||
performance.measure("tracing", "begin tracing"); | ||
} | ||
|
||
showLoaders(); | ||
await fetchData(); | ||
displayButton(); |
13 changes: 13 additions & 0 deletions
13
docs/examples/client-side-use-on-web-browser/package-lock.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"name": "client-side-use-on-web-browser", | ||
"version": "1.0.0", | ||
"private": true, | ||
"description": "Client-side use on Web Browser", | ||
"main": "server.js", | ||
"author": "Darshan Sen", | ||
"license": "MIT" | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// Tiny HTTP static file server | ||
// Refs: https://stackoverflow.com/a/29046869 | ||
|
||
const fs = require("node:fs"); | ||
const http = require("node:http"); | ||
const path = require("node:path"); | ||
|
||
http.createServer(function(request, response) { | ||
console.log(`Request url: "${request.url}"`); | ||
|
||
if (request.url === "/api/perftrace") { | ||
console.log("Serving perftrace endpoint"); | ||
|
||
request.setEncoding('utf8'); | ||
let rawData = ''; | ||
request.on('data', (chunk) => { rawData += chunk; }); | ||
request.on('end', () => { | ||
response.end(); | ||
console.log(rawData); | ||
fs.writeFileSync("events.json", rawData); | ||
this.close(); | ||
}); | ||
return; | ||
} | ||
|
||
if (request.url === "/api/submit") { | ||
console.log("Serving submission endpoint"); | ||
setTimeout(() => { | ||
response.end(JSON.stringify({ | ||
data: "Submit trace" | ||
})); | ||
}, 250); | ||
return; | ||
} | ||
|
||
const data = [ | ||
{ name: "do", timeout: 270 }, | ||
{ name: "re", timeout: 311 }, | ||
{ name: "mi", timeout: 622 }, | ||
{ name: "fa", timeout: 808 }, | ||
{ name: "so", timeout: 234 }, | ||
{ name: "la", timeout: 245 }, | ||
{ name: "ti", timeout: 300 }, | ||
{ name: "to", timeout: 690 }, | ||
]; | ||
|
||
const apiDataPrefix = "/api/data"; | ||
if (request.url.startsWith(apiDataPrefix)) { | ||
const index = request.url.slice(apiDataPrefix.length); | ||
const value = data[index]; | ||
if (!value) { | ||
console.log(`Error serving data ${index} endpoint`); | ||
response.writeHead(404, { 'Content-Type': contentType }); | ||
response.end(`${index} not found`); | ||
return; | ||
} | ||
console.log(`Serving data ${index} endpoint`); | ||
setTimeout(() => { | ||
response.end(JSON.stringify({ | ||
text: value.name, | ||
background: value.background, | ||
color: value.color | ||
})); | ||
}, value.timeout); | ||
return; | ||
} | ||
|
||
let filePath = '.' + request.url; | ||
if (filePath == './') { | ||
filePath = './index.html'; | ||
} else if (filePath == './perftrace.mjs') { | ||
filePath = '../../../index.mjs'; | ||
} | ||
const extname = path.extname(filePath); | ||
let contentType = 'text/html'; | ||
switch (extname) { | ||
case '.js': | ||
case '.mjs': | ||
contentType = 'text/javascript'; | ||
break; | ||
case '.css': | ||
contentType = 'text/css'; | ||
break; | ||
case '.json': | ||
contentType = 'application/json'; | ||
break; | ||
case '.png': | ||
contentType = 'image/png'; | ||
break; | ||
case '.jpg': | ||
contentType = 'image/jpg'; | ||
break; | ||
case '.wav': | ||
contentType = 'audio/wav'; | ||
break; | ||
} | ||
|
||
fs.readFile(filePath, function(error, content) { | ||
if (error) { | ||
console.log(`Error serving file "${filePath}": ${error.code}`); | ||
response.writeHead(404, { 'Content-Type': contentType }); | ||
response.end(`File not found: ${error.code}`); | ||
return; | ||
} | ||
|
||
console.log(`Serving file: "${filePath}"`); | ||
response.writeHead(200, { 'Content-Type': contentType }); | ||
response.end(content, 'utf-8'); | ||
}); | ||
}).listen(8080); | ||
|
||
console.log("Server running at http://localhost:8080"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
body { | ||
background-color: mistyrose; | ||
} | ||
|
||
/* | ||
* Loaded CSS | ||
* Refs: https://www.w3schools.com/howto/howto_css_loader.asp | ||
*/ | ||
|
||
.loader { | ||
border: 6px solid #f3f3f3; | ||
border-radius: 50%; | ||
border-top: 6px solid #3498db; | ||
width: 30px; | ||
height: 30px; | ||
-webkit-animation: spin 2s linear infinite; /* Safari */ | ||
animation: spin 2s linear infinite; | ||
|
||
/* Positioning the loaders at the same position as the loading text. */ | ||
margin-top: -20px; | ||
margin-bottom: 25px; | ||
} | ||
|
||
/* Safari */ | ||
@-webkit-keyframes spin { | ||
0% { -webkit-transform: rotate(0deg); } | ||
100% { -webkit-transform: rotate(360deg); } | ||
} | ||
|
||
@keyframes spin { | ||
0% { transform: rotate(0deg); } | ||
100% { transform: rotate(360deg); } | ||
} | ||
|
||
.data { | ||
font-size: 30px; | ||
} | ||
|
||
.submit { | ||
font-size: 25px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
export class TraceEvents { | ||
_eventObjects; | ||
_observer; | ||
|
||
constructor() { | ||
this._eventObjects = []; | ||
this._observer = new PerformanceObserver((perfEntryList) => { | ||
const measures = perfEntryList.getEntriesByType('measure'); | ||
measures.forEach((measure) => { | ||
this._eventObjects.push({ | ||
name: measure.name, | ||
cat: measure.entryType, | ||
ph: "X", | ||
pid: 1, | ||
ts: Math.round(measure.startTime * 1000), | ||
dur: Math.round(measure.duration * 1000), | ||
}); | ||
}); | ||
}); | ||
|
||
this._observer.observe({ type: 'measure', buffered: true }); | ||
} | ||
|
||
destroy() { | ||
this._observer.disconnect(); | ||
} | ||
|
||
getEvents() { | ||
return this._eventObjects; | ||
} | ||
} |
Oops, something went wrong.