add web
This commit is contained in:
parent
6c11e01841
commit
4f9a671bdc
@ -295,8 +295,8 @@ public:
|
|||||||
auto parse_val() -> Res<std::unique_ptr<Value>>;
|
auto parse_val() -> Res<std::unique_ptr<Value>>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline auto unexpected_tok_err(TokTyp expected, std::string_view msg)
|
inline auto unexpected_tok_err(
|
||||||
-> Res<std::unique_ptr<Value>>
|
TokTyp expected, std::string_view msg) -> Res<std::unique_ptr<Value>>
|
||||||
{
|
{
|
||||||
return Err {
|
return Err {
|
||||||
.pos = this->cur.val().pos,
|
.pos = this->cur.val().pos,
|
||||||
@ -339,7 +339,12 @@ public:
|
|||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
auto add_comma_seperated(const T& values, F f)
|
auto add_comma_seperated(const T& values, F f)
|
||||||
{
|
{
|
||||||
|
auto first = true;
|
||||||
for (const auto& value : values) {
|
for (const auto& value : values) {
|
||||||
|
if (!first) {
|
||||||
|
add(",");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
f(*this, value);
|
f(*this, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,4 +174,6 @@ int main()
|
|||||||
std::cout << std::format("done\n{}\n", vm.stack_repr_string(4));
|
std::cout << std::format("done\n{}\n", vm.stack_repr_string(4));
|
||||||
auto flame_graph = vm.flame_graph_json();
|
auto flame_graph = vm.flame_graph_json();
|
||||||
std::cout << std::format("flame graph: {}\n", flame_graph);
|
std::cout << std::format("flame graph: {}\n", flame_graph);
|
||||||
|
auto code_coverage = vm.code_coverage_json();
|
||||||
|
std::cout << std::format("code coverage: {}\n", code_coverage);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ void FlameGraphBuilder::fg_node_to_json(
|
|||||||
writer << "{\"fn\":" << std::to_string(node.fn)
|
writer << "{\"fn\":" << std::to_string(node.fn)
|
||||||
<< ",\"acc\":" << std::to_string(node.acc)
|
<< ",\"acc\":" << std::to_string(node.acc)
|
||||||
<< ",\"parent\":" << std::to_string(node.parent)
|
<< ",\"parent\":" << std::to_string(node.parent)
|
||||||
<< ",\"children:[\"";
|
<< ",\"children\":[";
|
||||||
auto first = true;
|
auto first = true;
|
||||||
for (auto child_index : node.children) {
|
for (auto child_index : node.children) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
|
@ -73,6 +73,7 @@ void VM::run_until_done()
|
|||||||
while (!done()) {
|
while (!done()) {
|
||||||
run_instruction();
|
run_instruction();
|
||||||
}
|
}
|
||||||
|
this->flame_graph.calculate_midway_result(this->instruction_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::run_n_instructions(size_t amount)
|
void VM::run_n_instructions(size_t amount)
|
||||||
@ -81,6 +82,7 @@ void VM::run_n_instructions(size_t amount)
|
|||||||
|
|
||||||
run_instruction();
|
run_instruction();
|
||||||
}
|
}
|
||||||
|
this->flame_graph.calculate_midway_result(this->instruction_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM::run_instruction()
|
void VM::run_instruction()
|
||||||
|
@ -56,6 +56,11 @@ public:
|
|||||||
this->current = this->nodes[this->current].parent;
|
this->current = this->nodes[this->current].parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void calculate_midway_result(int64_t ic)
|
||||||
|
{
|
||||||
|
calculate_node_midway_result(ic, this->current);
|
||||||
|
}
|
||||||
|
|
||||||
void to_json(json::Writer& writer) const override;
|
void to_json(json::Writer& writer) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -80,6 +85,16 @@ private:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void calculate_node_midway_result(int64_t ic, size_t node_index)
|
||||||
|
{
|
||||||
|
int64_t diff = ic - this->nodes[this->current].ic_start;
|
||||||
|
this->nodes[this->current].acc += diff;
|
||||||
|
this->nodes[this->current].ic_start = ic;
|
||||||
|
if (node_index == 0)
|
||||||
|
return;
|
||||||
|
calculate_node_midway_result(ic, this->nodes[this->current].parent);
|
||||||
|
}
|
||||||
|
|
||||||
void fg_node_to_json(json::Writer& writer, size_t node_index) const;
|
void fg_node_to_json(json::Writer& writer, size_t node_index) const;
|
||||||
|
|
||||||
std::vector<FGNode> nodes = { FGNode(0, 0) };
|
std::vector<FGNode> nodes = { FGNode(0, 0) };
|
||||||
|
162
web/deno.lock
162
web/deno.lock
@ -1,84 +1,90 @@
|
|||||||
{
|
{
|
||||||
"version": "3",
|
"version": "4",
|
||||||
"packages": {
|
"specifiers": {
|
||||||
"specifiers": {
|
"jsr:@oak/commons@1": "1.0.0",
|
||||||
"jsr:@oak/commons@^1.0": "jsr:@oak/commons@1.0.0",
|
"jsr:@oak/oak@*": "17.1.3",
|
||||||
"jsr:@oak/oak": "jsr:@oak/oak@17.1.3",
|
"jsr:@std/assert@1": "1.0.8",
|
||||||
"jsr:@std/assert@^1.0": "jsr:@std/assert@1.0.8",
|
"jsr:@std/bytes@1": "1.0.4",
|
||||||
"jsr:@std/bytes@^1.0": "jsr:@std/bytes@1.0.4",
|
"jsr:@std/bytes@^1.0.2": "1.0.4",
|
||||||
"jsr:@std/bytes@^1.0.2": "jsr:@std/bytes@1.0.4",
|
"jsr:@std/crypto@1": "1.0.3",
|
||||||
"jsr:@std/crypto@^1.0": "jsr:@std/crypto@1.0.3",
|
"jsr:@std/encoding@1": "1.0.5",
|
||||||
"jsr:@std/encoding@^1.0": "jsr:@std/encoding@1.0.5",
|
"jsr:@std/encoding@^1.0.5": "1.0.5",
|
||||||
"jsr:@std/encoding@^1.0.5": "jsr:@std/encoding@1.0.5",
|
"jsr:@std/http@1": "1.0.10",
|
||||||
"jsr:@std/http@^1.0": "jsr:@std/http@1.0.10",
|
"jsr:@std/io@0.224": "0.224.9",
|
||||||
"jsr:@std/io@0.224": "jsr:@std/io@0.224.9",
|
"jsr:@std/media-types@1": "1.1.0",
|
||||||
"jsr:@std/media-types@^1.0": "jsr:@std/media-types@1.1.0",
|
"jsr:@std/path@1": "1.0.8",
|
||||||
"jsr:@std/path@^1.0": "jsr:@std/path@1.0.8",
|
"npm:@types/node@*": "22.5.4",
|
||||||
"npm:path-to-regexp@6.2.1": "npm:path-to-regexp@6.2.1"
|
"npm:path-to-regexp@6.2.1": "6.2.1"
|
||||||
|
},
|
||||||
|
"jsr": {
|
||||||
|
"@oak/commons@1.0.0": {
|
||||||
|
"integrity": "49805b55603c3627a9d6235c0655aa2b6222d3036b3a13ff0380c16368f607ac",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert",
|
||||||
|
"jsr:@std/bytes@1",
|
||||||
|
"jsr:@std/crypto",
|
||||||
|
"jsr:@std/encoding@1",
|
||||||
|
"jsr:@std/http",
|
||||||
|
"jsr:@std/media-types"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"jsr": {
|
"@oak/oak@17.1.3": {
|
||||||
"@oak/commons@1.0.0": {
|
"integrity": "d89296c22db91681dd3a2a1e1fd14e258d0d5a9654de55637aee5b661c159f33",
|
||||||
"integrity": "49805b55603c3627a9d6235c0655aa2b6222d3036b3a13ff0380c16368f607ac",
|
"dependencies": [
|
||||||
"dependencies": [
|
"jsr:@oak/commons",
|
||||||
"jsr:@std/assert@^1.0",
|
"jsr:@std/assert",
|
||||||
"jsr:@std/bytes@^1.0",
|
"jsr:@std/bytes@1",
|
||||||
"jsr:@std/crypto@^1.0",
|
"jsr:@std/crypto",
|
||||||
"jsr:@std/encoding@^1.0",
|
"jsr:@std/http",
|
||||||
"jsr:@std/http@^1.0",
|
"jsr:@std/io",
|
||||||
"jsr:@std/media-types@^1.0"
|
"jsr:@std/media-types",
|
||||||
]
|
"jsr:@std/path",
|
||||||
},
|
"npm:path-to-regexp"
|
||||||
"@oak/oak@17.1.3": {
|
]
|
||||||
"integrity": "d89296c22db91681dd3a2a1e1fd14e258d0d5a9654de55637aee5b661c159f33",
|
|
||||||
"dependencies": [
|
|
||||||
"jsr:@oak/commons@^1.0",
|
|
||||||
"jsr:@std/assert@^1.0",
|
|
||||||
"jsr:@std/bytes@^1.0",
|
|
||||||
"jsr:@std/crypto@^1.0",
|
|
||||||
"jsr:@std/http@^1.0",
|
|
||||||
"jsr:@std/io@0.224",
|
|
||||||
"jsr:@std/media-types@^1.0",
|
|
||||||
"jsr:@std/path@^1.0",
|
|
||||||
"npm:path-to-regexp@6.2.1"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@std/assert@1.0.8": {
|
|
||||||
"integrity": "ebe0bd7eb488ee39686f77003992f389a06c3da1bbd8022184804852b2fa641b"
|
|
||||||
},
|
|
||||||
"@std/bytes@1.0.4": {
|
|
||||||
"integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc"
|
|
||||||
},
|
|
||||||
"@std/crypto@1.0.3": {
|
|
||||||
"integrity": "a2a32f51ddef632d299e3879cd027c630dcd4d1d9a5285d6e6788072f4e51e7f"
|
|
||||||
},
|
|
||||||
"@std/encoding@1.0.5": {
|
|
||||||
"integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04"
|
|
||||||
},
|
|
||||||
"@std/http@1.0.10": {
|
|
||||||
"integrity": "4e32d11493ab04e3ef09f104f0cb9beb4228b1d4b47c5469573c2c294c0d3692",
|
|
||||||
"dependencies": [
|
|
||||||
"jsr:@std/encoding@^1.0.5"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@std/io@0.224.9": {
|
|
||||||
"integrity": "4414664b6926f665102e73c969cfda06d2c4c59bd5d0c603fd4f1b1c840d6ee3",
|
|
||||||
"dependencies": [
|
|
||||||
"jsr:@std/bytes@^1.0.2"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@std/media-types@1.1.0": {
|
|
||||||
"integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"
|
|
||||||
},
|
|
||||||
"@std/path@1.0.8": {
|
|
||||||
"integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"npm": {
|
"@std/assert@1.0.8": {
|
||||||
"path-to-regexp@6.2.1": {
|
"integrity": "ebe0bd7eb488ee39686f77003992f389a06c3da1bbd8022184804852b2fa641b"
|
||||||
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
|
},
|
||||||
"dependencies": {}
|
"@std/bytes@1.0.4": {
|
||||||
}
|
"integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc"
|
||||||
|
},
|
||||||
|
"@std/crypto@1.0.3": {
|
||||||
|
"integrity": "a2a32f51ddef632d299e3879cd027c630dcd4d1d9a5285d6e6788072f4e51e7f"
|
||||||
|
},
|
||||||
|
"@std/encoding@1.0.5": {
|
||||||
|
"integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04"
|
||||||
|
},
|
||||||
|
"@std/http@1.0.10": {
|
||||||
|
"integrity": "4e32d11493ab04e3ef09f104f0cb9beb4228b1d4b47c5469573c2c294c0d3692",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/encoding@^1.0.5"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/io@0.224.9": {
|
||||||
|
"integrity": "4414664b6926f665102e73c969cfda06d2c4c59bd5d0c603fd4f1b1c840d6ee3",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/bytes@^1.0.2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/media-types@1.1.0": {
|
||||||
|
"integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"
|
||||||
|
},
|
||||||
|
"@std/path@1.0.8": {
|
||||||
|
"integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remote": {}
|
"npm": {
|
||||||
|
"@types/node@22.5.4": {
|
||||||
|
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
|
||||||
|
"dependencies": [
|
||||||
|
"undici-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"path-to-regexp@6.2.1": {
|
||||||
|
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw=="
|
||||||
|
},
|
||||||
|
"undici-types@6.19.8": {
|
||||||
|
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Application, Router } from "jsr:@oak/oak";
|
import { Application, Router } from "jsr:@oak/oak";
|
||||||
|
|
||||||
|
|
||||||
const app = new Application();
|
const app = new Application();
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
9
web/public/deno.jsonc
Normal file
9
web/public/deno.jsonc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": false,
|
||||||
|
"lib": ["dom", "dom.iterable", "dom.asynciterable"],
|
||||||
|
},
|
||||||
|
"fmt": {
|
||||||
|
"indentWidth": 4
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,14 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
</head>
|
<link rel="stylesheet" href="style.css">
|
||||||
<body>
|
<script src="index.js" type="module" defer></script>
|
||||||
<h1>slige</h1>
|
</head>
|
||||||
</body>
|
<body>
|
||||||
|
<main>
|
||||||
|
<div id="code-coverage"></div>
|
||||||
|
<div id="flame-graph"></div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
179
web/public/index.js
Normal file
179
web/public/index.js
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
const codeCoverageDiv = document.querySelector("#code-coverage");
|
||||||
|
const flameGraphDiv = document.querySelector("#flame-graph");
|
||||||
|
|
||||||
|
function loadCodeCoverage(text, codeCoverageData) {
|
||||||
|
codeCoverageDiv.innerHTML = `
|
||||||
|
<canvas id="code-coverage-canvas"></canvas>
|
||||||
|
<pre><code>${text}</code></pre>
|
||||||
|
<span id="covers-tooltip" hidden></span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
/** @type { HTMLCanvasElement } */
|
||||||
|
const canvas = document.querySelector("#code-coverage-canvas");
|
||||||
|
canvas.width = 1000;
|
||||||
|
canvas.height = 500;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
ctx.font = "20px monospace";
|
||||||
|
|
||||||
|
const { width: chWidth } = ctx.measureText("-");
|
||||||
|
const chHeight = 26;
|
||||||
|
|
||||||
|
const color = (ratio) =>
|
||||||
|
`rgba(${255 - 255 * ratio}, ${255 * ratio}, 125, 0.5)`;
|
||||||
|
|
||||||
|
const entries = codeCoverageData.toSorted((a, b) => b.index - a.index);
|
||||||
|
|
||||||
|
const charEntries = {};
|
||||||
|
|
||||||
|
let line = 1;
|
||||||
|
let col = 1;
|
||||||
|
for (let index = 0; index < text.length; ++index) {
|
||||||
|
const entry = entries.find((entry) => index >= entry.index);
|
||||||
|
charEntries[`${line}-${col}`] = entry;
|
||||||
|
ctx.fillStyle = color(Math.min(entry.covers / 25, 1));
|
||||||
|
ctx.fillRect(
|
||||||
|
(col - 1) * chWidth,
|
||||||
|
(line - 1) * chHeight,
|
||||||
|
chWidth,
|
||||||
|
chHeight,
|
||||||
|
);
|
||||||
|
col += 1;
|
||||||
|
if (text[index] == "\n") {
|
||||||
|
col = 1;
|
||||||
|
line += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tooltip = document.getElementById("covers-tooltip");
|
||||||
|
|
||||||
|
canvas.addEventListener("mousemove", (e) => {
|
||||||
|
const col = Math.floor(e.offsetX / chWidth + 1);
|
||||||
|
const line = Math.floor(e.offsetY / chHeight + 1);
|
||||||
|
const key = `${line}-${col}`;
|
||||||
|
if (!(key in charEntries)) {
|
||||||
|
tooltip.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const entry = charEntries[key];
|
||||||
|
tooltip.innerText = `Ran ${entry.covers} time${entry.covers !== 1 ? "s" : ""
|
||||||
|
}`;
|
||||||
|
tooltip.style.left = `${e.clientX + 20}px`;
|
||||||
|
tooltip.style.top = `${e.clientY + 20}px`;
|
||||||
|
tooltip.hidden = false;
|
||||||
|
});
|
||||||
|
canvas.addEventListener("mouseleave", () => {
|
||||||
|
tooltip.hidden = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadFlameGraph(flameGraphData, fnNames) {
|
||||||
|
flameGraphDiv.innerHTML = `
|
||||||
|
<canvas id="flame-graph-canvas"></canvas>
|
||||||
|
<span id="flame-graph-tooltip" hidden></span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
/** @type { HTMLCanvasElement } */
|
||||||
|
const canvas = document.querySelector("#flame-graph-canvas");
|
||||||
|
canvas.width = 1000;
|
||||||
|
canvas.height = 500;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
ctx.font = "16px monospace";
|
||||||
|
|
||||||
|
const nodes = [];
|
||||||
|
|
||||||
|
function calculateNodeRects(node, depth, totalAcc, offsetAcc) {
|
||||||
|
const x = (offsetAcc / totalAcc) * canvas.width;
|
||||||
|
const y = canvas.height - 30 * depth - 30;
|
||||||
|
const w = ((node.acc + 1) / totalAcc) * canvas.width;
|
||||||
|
const h = 30;
|
||||||
|
|
||||||
|
const title = fnNames[node.fn];
|
||||||
|
const percent = `${(node.acc / totalAcc * 100).toFixed(1)}%`;
|
||||||
|
nodes.push({ x, y, w, h, title, percent });
|
||||||
|
|
||||||
|
const totalChildrenAcc = node.children.reduce(
|
||||||
|
(acc, child) => acc + child.acc,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
let newOffsetAcc = offsetAcc + (node.acc - totalChildrenAcc) / 2;
|
||||||
|
for (const child of node.children) {
|
||||||
|
calculateNodeRects(child, depth + 1, totalAcc, newOffsetAcc);
|
||||||
|
newOffsetAcc += child.acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calculateNodeRects(flameGraphData, 0, flameGraphData.acc, 0);
|
||||||
|
|
||||||
|
for (const node of nodes) {
|
||||||
|
const { x, y, w, h, title } = node;
|
||||||
|
ctx.fillStyle = "rgb(255, 125, 0)";
|
||||||
|
ctx.fillRect(
|
||||||
|
x + 1,
|
||||||
|
y + 1,
|
||||||
|
w - 2,
|
||||||
|
h - 2,
|
||||||
|
);
|
||||||
|
ctx.fillStyle = "black";
|
||||||
|
ctx.fillText(
|
||||||
|
title,
|
||||||
|
(x + (w - 10) / 2 - ctx.measureText(title).width / 2) + 5,
|
||||||
|
y + 20,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tooltip = document.getElementById("flame-graph-tooltip");
|
||||||
|
|
||||||
|
canvas.addEventListener("mousemove", (e) => {
|
||||||
|
const x = e.offsetX;
|
||||||
|
const y = e.offsetY;
|
||||||
|
const node = nodes.find((node) =>
|
||||||
|
x >= node.x && x < node.x + node.w && y >= node.y &&
|
||||||
|
y < node.y + node.h
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
tooltip.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tooltip.innerText = `${node.title} ${node.percent}`;
|
||||||
|
tooltip.style.left = `${e.clientX + 20}px`;
|
||||||
|
tooltip.style.top = `${e.clientY + 20}px`;
|
||||||
|
tooltip.hidden = false;
|
||||||
|
});
|
||||||
|
canvas.addEventListener("mouseleave", () => {
|
||||||
|
tooltip.hidden = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeData = `\
|
||||||
|
fn add(a, b) {
|
||||||
|
+ a b
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = 0;
|
||||||
|
let i = 0;
|
||||||
|
loop {
|
||||||
|
if >= i 10 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = add(result, 5);
|
||||||
|
i = + i 1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const codeCoverageData = JSON.parse(
|
||||||
|
`[{"index":0,"line":1,"col":1,"covers":2},{"index":28,"line":5,"col":1,"covers":1},{"index":44,"line":6,"col":1,"covers":1},{"index":55,"line":7,"col":1,"covers":1},{"index":66,"line":8,"col":5,"covers":11},{"index":104,"line":11,"col":5,"covers":10},{"index":19,"line":2,"col":5,"covers":10},{"index":133,"line":12,"col":5,"covers":10},{"index":87,"line":9,"col":9,"covers":1}]`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const flameGraphData = JSON.parse(
|
||||||
|
`{"fn":0,"acc":257,"parent":0,"children":[{"fn":18,"acc":251,"parent":0,"children":[{"fn":12,"acc":30,"parent":1,"children":[]}]}]}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
loadCodeCoverage(codeData, codeCoverageData);
|
||||||
|
loadFlameGraph(flameGraphData, {
|
||||||
|
0: "<entry>",
|
||||||
|
12: "add",
|
||||||
|
18: "main",
|
||||||
|
});
|
89
web/public/style.css
Normal file
89
web/public/style.css
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: light dark;
|
||||||
|
font-family: sans;
|
||||||
|
|
||||||
|
--bg-1: #2b2d31;
|
||||||
|
--bg-2: #313338;
|
||||||
|
--fg-2: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: var(--bg-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
max-width: 1500px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#code-coverage {
|
||||||
|
width: 1000px;
|
||||||
|
height: 500px;
|
||||||
|
margin: 20px;
|
||||||
|
background-color: rgb(240, 220, 200);
|
||||||
|
}
|
||||||
|
#code-coverage pre {
|
||||||
|
background-color: none;
|
||||||
|
}
|
||||||
|
#code-coverage code {
|
||||||
|
font-family: monospace;
|
||||||
|
color: black;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
#code-coverage canvas {
|
||||||
|
z-index: 1;
|
||||||
|
width: 1000px;
|
||||||
|
height: 500px;
|
||||||
|
position: absolute;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
#code-coverage #covers-tooltip {
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: var(--bg-2);
|
||||||
|
box-shadow: 2px 2px 2px black;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flame-graph {
|
||||||
|
width: 1004px;
|
||||||
|
height: 504px;
|
||||||
|
margin: 20px;
|
||||||
|
background-color: var(--bg-2);
|
||||||
|
border: 2px solid rgb(240, 220, 200);
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
#flame-graph canvas {
|
||||||
|
z-index: 1;
|
||||||
|
width: 1000px;
|
||||||
|
height: 500px;
|
||||||
|
position: absolute;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
transform: translate(-2px, -2px);
|
||||||
|
}
|
||||||
|
#flame-graph #flame-graph-tooltip {
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: var(--bg-2);
|
||||||
|
box-shadow: 2px 2px 2px black;
|
||||||
|
color: #eee;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user