117 lines
No EOL
3.2 KiB
TypeScript
117 lines
No EOL
3.2 KiB
TypeScript
export const program = `import { TextLineStream } from "https://deno.land/std@0.224.0/streams/text_line_stream.ts";
|
|
import type { Msg } from "./lib/types.ts";
|
|
|
|
async function main() {
|
|
const [sokcet_path] = Deno.args;
|
|
if (sokcet_path === undefined) throw new Error("Feur.");
|
|
|
|
const socket = Deno.listen({ transport: "unix", path: sokcet_path });
|
|
const connection = await socket.accept();
|
|
const lines = connection.readable
|
|
.pipeThrough(new TextDecoderStream())
|
|
.pipeThrough(new TextLineStream());
|
|
|
|
const stats = new Tracker();
|
|
stats.update([0, "total", Number(Temporal.Now.instant().epochNanoseconds)]);
|
|
try {
|
|
for await (const line of lines) stats.update(JSON.parse(line) as Msg);
|
|
} catch (_) { /* STDOUT closed */ }
|
|
stats.update([1, "total", Number(Temporal.Now.instant().epochNanoseconds)]);
|
|
|
|
stats.log();
|
|
}
|
|
|
|
class Tracker {
|
|
register;
|
|
|
|
constructor() {
|
|
this.register = new Map<string, SectionTracker>();
|
|
}
|
|
|
|
update(message: Msg) {
|
|
if (message[0] === 0) this.get_tracker_for(message[1]).tip(message[2]);
|
|
if (message[0] === 1) this.get_tracker_for(message[1]).top(message[2]);
|
|
if (message[0] === 2) this.log();
|
|
}
|
|
|
|
get_tracker_for(section: string) {
|
|
const stored = this.register.get(section);
|
|
if (stored !== undefined) return stored;
|
|
const new_ = new SectionTracker(section);
|
|
this.register.set(section, new_);
|
|
return new_;
|
|
}
|
|
|
|
log() {
|
|
const total = this.register.get("total")?.average();
|
|
for (const section of this.register.values()) section.log(total);
|
|
Deno.exit(0);
|
|
}
|
|
}
|
|
|
|
class SectionTracker {
|
|
name;
|
|
runs;
|
|
current_tip;
|
|
|
|
constructor(name: string) {
|
|
this.name = name;
|
|
this.runs = [] as number[];
|
|
this.current_tip = null as number | null;
|
|
}
|
|
|
|
tip(timestamp_ns: number) {
|
|
if (this.current_tip !== null) throw new Error(\`Second TIP before TOP on section \${this.name}.\`);
|
|
this.current_tip = timestamp_ns;
|
|
}
|
|
|
|
top(timestamp_ns: number) {
|
|
if (this.current_tip === null) throw new Error(\`TOP before TIP on section \${this.name}.\`);
|
|
const duration = timestamp_ns - this.current_tip;
|
|
this.current_tip = null;
|
|
this.runs.push(duration);
|
|
}
|
|
|
|
log(total_average: number | undefined) {
|
|
const count = this.runs.length;
|
|
const avg = this.average();
|
|
const cumul = count * this.average();
|
|
const fraciton = Math.floor((total_average ? cumul / total_average : 0) * 100);
|
|
const show_total = total_average !== undefined && this.name !== "total";
|
|
const columns = [
|
|
this.name,
|
|
\`\${count} runs\`,
|
|
\`\${to_human(avg)} avg\`,
|
|
\`\${to_human(cumul)} cumul\`,
|
|
...(show_total ? [\`\${fraciton} %\`] : []),
|
|
];
|
|
const line = columns.map((e) => e.padStart(18));
|
|
console.log(...line);
|
|
}
|
|
|
|
average() {
|
|
const total = this.runs.reduce((a, b) => a + b);
|
|
return total / this.runs.length;
|
|
}
|
|
}
|
|
|
|
function to_human(duration_ns: number) {
|
|
const units = [
|
|
[3_600_000_000_000, "h"],
|
|
[60_000_000_000, "min"],
|
|
[1_000_000_000, "s"],
|
|
[1_000_000, "ms"],
|
|
[1_000, "µs"],
|
|
[1, "ns"],
|
|
] as const;
|
|
for (const [fac, name] of units) {
|
|
if ((duration_ns / fac) < 1.) continue;
|
|
const mapped_duration = duration_ns / fac;
|
|
const [integral, decimal] = mapped_duration.toString().split(".");
|
|
const formatted = \`\${integral}.\${decimal.slice(0, 3).padEnd(3, "0")}\`;
|
|
return \`\${formatted} \${name}\`;
|
|
}
|
|
}
|
|
|
|
if (import.meta.main) await main();
|
|
`; |