Skip to content

Commit

Permalink
feat: add facility for capturing and graphing kernel stats
Browse files Browse the repository at this point in the history
  • Loading branch information
FUDCo committed May 27, 2020
1 parent 1ea7bb7 commit 0df83b3
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 12 deletions.
54 changes: 52 additions & 2 deletions packages/stat-logger/src/statGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,57 @@ import fs from 'fs';
import path from 'path';
import * as vega from 'vega';

export function initGraphSpec(statsPath, xField, xLabel, yField, yLabel) {
function scanMax(filePath, fields) {
const lines = fs.readFileSync(filePath, { encoding: 'utf8' }).split('\n');
const headers = lines[0].split('\t');
const headerMap = [];
headerMap.length = headers.length;
for (const field of fields) {
let hit = -1;
for (let col = 0; col < headers.length; col += 1) {
if (field === headers[col]) {
hit = col;
break;
}
}
if (hit < 0) {
throw new Error(`field ${field} not found in ${filePath}`);
} else {
headerMap[hit] = field;
}
}
let maxValue = -1;
let maxField;
for (let row = 1; row < lines.length; row += 1) {
const line = lines[row].split('\t');
for (let col = 0; col < headerMap.length; col += 1) {
if (headerMap[col] && line[col] > maxValue) {
maxValue = line[col];
maxField = headerMap[col];
}
}
}
return { field: maxField, value: maxValue, filePath };
}

function scanMaxFile(filePaths, fields) {
let max = {
value: -1,
field: null,
filePath: null,
};
for (const filePath of filePaths) {
const fileMax = scanMax(filePath, fields);
if (fileMax.value > max.value) {
max = fileMax;
}
}
return max;
}

export function initGraphSpec(filePaths, xField, xLabel, yFields, yLabel) {
const maxGraph = scanMaxFile(filePaths, yFields);
const statsPath = maxGraph.filePath;
const spec = {
$schema: 'https://vega.github.io/schema/vega/v5.json',
width: 1500,
Expand Down Expand Up @@ -31,7 +81,7 @@ export function initGraphSpec(statsPath, xField, xLabel, yField, yLabel) {
range: 'height',
nice: true,
zero: true,
domain: { data: statsPath, field: yField },
domain: { data: statsPath, field: maxGraph.field },
},
{
name: 'legend',
Expand Down
5 changes: 4 additions & 1 deletion packages/stat-logger/src/statLogger.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import process from 'process';

export function makeStatLogger(tag, headers, options) {
const dir = (options && options.dir) || '.';
const statsPath = `${dir}/stats-${tag}-${process.pid}`;
if (!tag) {
tag = `${process.pid}`;
}
const statsPath = `${dir}/stats-${tag}`;
const out = fs.createWriteStream(statsPath);
out.write(headers.join('\t'));
out.write('\n');
Expand Down
1 change: 1 addition & 0 deletions packages/swingset-runner/bin/graphStats
71 changes: 65 additions & 6 deletions packages/swingset-runner/src/dataGraphApp.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path';
import process from 'process';

import {
Expand All @@ -17,14 +18,51 @@ const colors = [
[ '#16e7cf', '#73fdea', '#00a89d', '#2e578c' ],
];

export async function dataGraphApp(xField, xLabel, yField, yLabel, lineFields) {
const appName = path.basename(process.argv[1]);

function usage(showFields) {
let fieldsLine = '';
if (showFields) {
fieldsLine =
'\n --fields FIELDS - comma separated list of field names to graph';
}

console.log(`
Command line:
${appName} [FLAGS...] ARGS...
FLAGS may be:
--output FILE - place output in FILE (otherwise it will got to stdout)
-o FILE - synonym for --output
--pdf - generate a PDF file (PNG by default)
--label STR - label for a data file
-l STR - synonym for --label${fieldsLine}
--help - print this helpful usage information
ARGS consist of paths to one or more data files. Each datafile must contain
all the fields being graphed. Files may be labeled in the graph key with the
--label option, one --label option per file, applied in the same order as the
data file ARGS (and may be interleaved with them on the command line).
`);
}

function fail(message, printUsage, showFields) {
console.log(message);
if (printUsage) {
usage(showFields);
}
process.exit(1);
}

export async function dataGraphApp(xField, xLabel, yField, yLabel, fields) {
const argv = process.argv.splice(2);

let outfile = null;
const datafiles = [];
const tags = [];
let type = 'png';

const expectFields = !fields;
while (argv[0]) {
const arg = argv.shift();
if (arg.startsWith('-')) {
Expand All @@ -40,8 +78,19 @@ export async function dataGraphApp(xField, xLabel, yField, yLabel, lineFields) {
case '-l':
tags.push(argv.shift());
break;
case '--help':
usage(expectFields);
process.exit(0);
break;
case '--fields':
case '-f':
if (expectFields) {
fields = argv.shift().split(',');
break;
}
// note fall through to error
default:
throw new Error(`invalid flag ${arg}`);
fail(`invalid flag ${arg}`, true, expectFields);
}
} else {
datafiles.push(arg);
Expand All @@ -50,20 +99,30 @@ export async function dataGraphApp(xField, xLabel, yField, yLabel, lineFields) {
}
}
}
if (!fields) {
fail('you must specify some field names to graph', true, expectFields);
}
if (datafiles.length < 1) {
throw new Error('you must specify some input');
fail('you must specify some input', true, expectFields);
}

let yFields;
if (expectFields) {
yFields = fields;
} else {
yFields = [yField];
}

const spec = initGraphSpec(datafiles[0], xField, xLabel, yField, yLabel);
const spec = initGraphSpec(datafiles, xField, xLabel, yFields, yLabel);
for (let dataIdx = 0; dataIdx < datafiles.length; dataIdx += 1) {
addDataToGraphSpec(spec, datafiles[dataIdx]);
const groupColors = colors[dataIdx % colors.length];
for (let lineIdx = 0; lineIdx < lineFields.length; lineIdx += 1) {
for (let lineIdx = 0; lineIdx < fields.length; lineIdx += 1) {
addGraphToGraphSpec(
spec,
tags[dataIdx],
datafiles[dataIdx],
lineFields[lineIdx],
fields[lineIdx],
groupColors[lineIdx % groupColors.length],
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/swingset-runner/src/dumpstore.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function dumpStore(store, outfile, rawMode) {

popt('runQueue');
popt('crankNumber');
popt('kernelStats');
gap();

p('// device info');
Expand Down
16 changes: 16 additions & 0 deletions packages/swingset-runner/src/graphStats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env -S node -r esm

import { dataGraphApp } from './dataGraphApp';

export async function main() {
// prettier-ignore
await dataGraphApp(
'block',
'Block #',
null,
'Count',
null,
);
}

main();
25 changes: 22 additions & 3 deletions packages/swingset-runner/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ FLAGS may be:
--logtimes - log block execution time stats while running
--logmem - log memory usage stats after each block
--logdisk - log disk space usage stats after each block
--logall - log block times, memory use, and disk space
--logstats - log kernel stats after each block
--logall - log kernel stats, block times, memory use, and disk space
--logtag STR - tag for stats log file (default "runner")
--forcegc - run garbage collector after each block
--batchsize N - set BATCHSIZE to N (default 200)
--verbose - output verbose debugging messages as it runs
Expand Down Expand Up @@ -91,6 +93,8 @@ export async function main() {
let logTimes = false;
let logMem = false;
let logDisk = false;
let logStats = false;
let logTag = 'runner';
let forceGC = false;
let verbose = false;
let doDumps = false;
Expand All @@ -114,10 +118,17 @@ export async function main() {
case '--logdisk':
logDisk = true;
break;
case '--logstats':
logStats = true;
break;
case '--logall':
logTimes = true;
logMem = true;
logDisk = true;
logStats = true;
break;
case '--logtag':
logTag = argv.shift();
break;
case '--forcegc':
forceGC = true;
Expand Down Expand Up @@ -223,6 +234,8 @@ export async function main() {
config.verbose = true;
}

const controller = await buildVatController(config, true, bootstrapArgv);

let blockNumber = 0;
let statLogger = null;
if (logTimes || logMem || logDisk) {
Expand All @@ -236,11 +249,14 @@ export async function main() {
if (logDisk) {
headers.push('disk');
}
statLogger = makeStatLogger('runner', headers);
if (logStats) {
const statNames = Object.getOwnPropertyNames(controller.getStats());
headers = headers.concat(statNames);
}
statLogger = makeStatLogger(logTag, headers);
}

let crankNumber = 0;
const controller = await buildVatController(config, true, bootstrapArgv);
switch (command) {
case 'run': {
await commandRun(0, blockMode);
Expand Down Expand Up @@ -376,6 +392,9 @@ export async function main() {
const diskUsage = dbMode === '--lmdb' ? store.diskUsage() : 0;
data.push(diskUsage);
}
if (logStats) {
data = data.concat(Object.values(controller.getStats()));
}
statLogger.log(data);
}
return actualSteps;
Expand Down

0 comments on commit 0df83b3

Please sign in to comment.