fix: Fix multiple bugs with new authentication flow
This commit is contained in:
202
tools/perf-diag-viewer.js
Normal file
202
tools/perf-diag-viewer.js
Normal file
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
function readArg(name) {
|
||||
const index = args.indexOf(name);
|
||||
|
||||
if (index === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return args[index + 1] ?? null;
|
||||
}
|
||||
|
||||
function hasFlag(name) {
|
||||
return args.includes(name);
|
||||
}
|
||||
|
||||
function resolveCandidateUserDataDirs() {
|
||||
const home = os.homedir();
|
||||
const appNames = ['Toju', 'metoyou'];
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const base = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
||||
|
||||
return appNames.map((name) => path.join(base, name));
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const base = path.join(home, 'Library', 'Application Support');
|
||||
|
||||
return appNames.map((name) => path.join(base, name));
|
||||
}
|
||||
|
||||
const base = process.env.XDG_CONFIG_HOME || path.join(home, '.config');
|
||||
|
||||
return appNames.map((name) => path.join(base, name));
|
||||
}
|
||||
|
||||
function resolveDefaultDiagnosticsDir() {
|
||||
const candidates = resolveCandidateUserDataDirs()
|
||||
.map((userDataDir) => path.join(userDataDir, 'diagnostics'))
|
||||
.filter((dir) => fs.existsSync(dir));
|
||||
|
||||
if (candidates.length === 0) {
|
||||
return path.join(resolveCandidateUserDataDirs()[0], 'diagnostics');
|
||||
}
|
||||
|
||||
return candidates
|
||||
.map((dir) => ({ dir, mtimeMs: fs.statSync(dir).mtimeMs }))
|
||||
.sort((left, right) => right.mtimeMs - left.mtimeMs)[0].dir;
|
||||
}
|
||||
|
||||
function parseJsonLines(content) {
|
||||
return content
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function formatKb(kb) {
|
||||
if (kb == null) {
|
||||
return 'n/a';
|
||||
}
|
||||
|
||||
return `${Math.round(kb / 1024)} MB`;
|
||||
}
|
||||
|
||||
function summarize(entries) {
|
||||
const latestProcess = [...entries].reverse().find((entry) => entry.type === 'process');
|
||||
const latestStore = [...entries].reverse().find((entry) => entry.type === 'store');
|
||||
const latestComponents = [...entries].reverse().find((entry) => entry.type === 'components');
|
||||
const latestHeap = [...entries].reverse().find((entry) => entry.type === 'heap');
|
||||
|
||||
if (latestProcess) {
|
||||
console.log(`Process RSS total: ${formatKb(latestProcess.payload.totalWorkingSetKb)}`);
|
||||
}
|
||||
|
||||
if (latestHeap) {
|
||||
console.log(`Renderer JS heap: ${latestHeap.payload.usedJsHeapMb ?? 'n/a'} MB`);
|
||||
}
|
||||
|
||||
if (latestStore?.payload?.domains) {
|
||||
console.log('Store domains (estimated bytes):');
|
||||
|
||||
for (const [domain, bytes] of Object.entries(latestStore.payload.domains).sort((a, b) => b[1] - a[1])) {
|
||||
console.log(` ${domain}: ${bytes}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (latestComponents?.payload?.domains) {
|
||||
console.log('Live components by domain:');
|
||||
|
||||
for (const [domain, count] of Object.entries(latestComponents.payload.domains).sort((a, b) => b[1] - a[1])) {
|
||||
console.log(` ${domain}: ${count}`);
|
||||
}
|
||||
}
|
||||
|
||||
const leaks = latestComponents?.payload?.suspectedLeaks;
|
||||
|
||||
if (Array.isArray(leaks) && leaks.length > 0) {
|
||||
console.log('Suspected component leaks:');
|
||||
|
||||
for (const leak of leaks) {
|
||||
console.log(` ${leak.name}: ${leak.count} (expected ${leak.expected})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const dir = readArg('--dir') || resolveDefaultDiagnosticsDir();
|
||||
const file = readArg('--file');
|
||||
|
||||
if (!fs.existsSync(dir) && !file) {
|
||||
console.error(`Diagnostics directory not found: ${dir}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const targetFile = file || findLatestFile(dir);
|
||||
|
||||
if (!targetFile) {
|
||||
console.error(`No diagnostics files found in ${dir}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Reading ${targetFile}`);
|
||||
|
||||
if (hasFlag('--tail')) {
|
||||
tailFile(targetFile);
|
||||
return;
|
||||
}
|
||||
|
||||
const entries = parseJsonLines(fs.readFileSync(targetFile, 'utf8'));
|
||||
summarize(entries);
|
||||
}
|
||||
|
||||
function findLatestFile(dir) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fs.readdirSync(dir)
|
||||
.filter((name) => name.startsWith('perf-') && name.endsWith('.jsonl'))
|
||||
.map((name) => path.join(dir, name))
|
||||
.sort((left, right) => fs.statSync(right).mtimeMs - fs.statSync(left).mtimeMs)[0] ?? null;
|
||||
}
|
||||
|
||||
function tailFile(targetFile) {
|
||||
let position = 0;
|
||||
|
||||
const printNewLines = () => {
|
||||
const stats = fs.statSync(targetFile);
|
||||
const nextSize = stats.size;
|
||||
|
||||
if (nextSize < position) {
|
||||
position = 0;
|
||||
}
|
||||
|
||||
if (nextSize === position) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fd = fs.openSync(targetFile, 'r');
|
||||
const length = nextSize - position;
|
||||
const buffer = Buffer.alloc(length);
|
||||
|
||||
fs.readSync(fd, buffer, 0, length, position);
|
||||
fs.closeSync(fd);
|
||||
position = nextSize;
|
||||
|
||||
for (const line of buffer.toString('utf8').split('\n')) {
|
||||
if (!line.trim()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const entry = JSON.parse(line);
|
||||
console.log(`[${entry.type}] ${JSON.stringify(entry.payload)}`);
|
||||
} catch {
|
||||
console.log(line);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
printNewLines();
|
||||
setInterval(printNewLines, 1000);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user