UNPKG

@oaklean/cli

Version:

A command-line interface that provides utilities for parsing, inspecting, and converting the .oak file format, as well as interfaces used in the @oaklean suite.

295 lines 29.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = __importStar(require("fs")); const profiler_1 = require("@oaklean/profiler"); const profiler_core_1 = require("@oaklean/profiler-core"); const commander_1 = require("commander"); class JestCommands { constructor() { const baseCommand = commander_1.program .command('jest') .description('Commands to inspect the jest profiler format. This is mostly used for debugging purposes'); baseCommand .command('verify') .description('Checks wether the accumulate report of the jest-test-environment would be generated the same way with this version') .option('-o, --output <output>', 'output file path of the reproduced report (if not set no file will be generated)') .option('-d, --deep', 'also check wether each report would be generated the same way') .option('-m, --measure', 'also measure the reproduction of the reports and outputs a report (this will take longer, but is useful for performance comparisons)') .action(this.verify.bind(this)); baseCommand .command('inspect-profiles') .description('Inspects all reports and cpu profiles in the jests output directory and verifies their consistency') .action(this.inspectCPUProfiles.bind(this)); baseCommand .command('verify-trees') .description('Checks all sub reports in the output directory for SourceFileMetaDataTree consistency') .action(this.verifyTrees.bind(this)); } static init() { return new JestCommands(); } verify(options) { return __awaiter(this, void 0, void 0, function* () { const profilerConfig = profiler_core_1.ProfilerConfig.autoResolve(); const rootDir = profilerConfig.getRootDir(); const exportAssetHelper = new profiler_core_1.ExportAssetHelper(profilerConfig.getOutDir().join('jest')); const verifyExportAssetHelper = new profiler_core_1.ExportAssetHelper(profilerConfig.getOutDir().join('jest-verify')); const reportPaths = exportAssetHelper.allReportPathsInOutputDir(); const accumulatedProjectReportPath = exportAssetHelper.outputAccumulatedReportPath(); const expectedAccumulatedReport = profiler_core_1.ProjectReport.loadFromFile(accumulatedProjectReportPath, 'bin'); if (!expectedAccumulatedReport) { profiler_core_1.LoggerHelper.warn(`Could not find a profiler report at ${accumulatedProjectReportPath.toPlatformString()}\n` + 'So no comparison can be made. Please make sure that the report is generated and stored in the original location.'); return; } let profiler; if (options.measure !== undefined) { profiler_core_1.LoggerHelper.success('Measuring the reproduction of the reports.'); profiler = new profiler_1.Profiler('verify'); yield profiler.start('latest'); } const reports = []; if (options.deep !== undefined) { if (fs.existsSync(verifyExportAssetHelper.outputDir().toString())) { fs.rmSync(verifyExportAssetHelper.outputDir().toString(), { recursive: true }); } for (const reportPath of reportPaths) { const expectedReport = profiler_core_1.ProjectReport.loadFromFile(reportPath, 'bin'); if (!expectedReport) { profiler_core_1.LoggerHelper.error(`ProjectReport could not be found: ${reportPath}`); continue; } if (reportPath.toString() === accumulatedProjectReportPath.toString()) { continue; // Skip the accumulated report itself } const title = exportAssetHelper.titleFromReportFilePath(reportPath); const report = new profiler_core_1.ProjectReport(expectedReport.executionDetails, profiler_core_1.ReportKind.measurement); const metricsDataCollectionPath = exportAssetHelper.outputMetricsDataCollectionPath(title); const externalResourceHelperPath = exportAssetHelper.outputExternalResourceHelperPath(title); const v8CPUProfilePath = exportAssetHelper.outputCPUProfilePath(title); const cpuProfile = yield profiler_core_1.CPUProfileHelper.loadFromFile(v8CPUProfilePath); if (cpuProfile === undefined) { profiler_core_1.LoggerHelper.error(`CPU profile could not be loaded from ${v8CPUProfilePath.toPlatformString()}. ` + 'Please make sure the file exists and is a valid CPU profile.'); continue; } const metricsDataCollection = profiler_core_1.MetricsDataCollection.loadFromFile(metricsDataCollectionPath); const externalResourceHelper = profiler_core_1.ExternalResourceHelper.loadFromFile(rootDir, externalResourceHelperPath); if (externalResourceHelper === undefined) { profiler_core_1.LoggerHelper.error('External Resource Helper could not be loaded from' + externalResourceHelperPath.toPlatformString() + ' Please make sure the file exists and is a valid external resource helper.'); continue; } profiler_core_1.LoggerHelper.log(`[REPRODUCE] ${reportPath.toPlatformString()}`); yield report.insertCPUProfile(rootDir, cpuProfile, externalResourceHelper, metricsDataCollection); report.trackUncommittedFiles(rootDir, externalResourceHelper); report.relativeRootDir = expectedReport.relativeRootDir; if (report.hash() !== expectedReport.hash()) { profiler_core_1.LoggerHelper.warn(`[NOT_REPRODUCIBLE] ${reportPath.toPlatformString()}`, report.hash(), expectedReport.hash()); report.storeToFile(verifyExportAssetHelper.outputReportPath(title), 'pretty-json', profilerConfig); expectedReport.storeToFile(verifyExportAssetHelper.outputReportPath(title + '-expected'), 'pretty-json', profilerConfig); } else { profiler_core_1.LoggerHelper.success(`[REPRODUCIBLE] ${reportPath.toPlatformString()}`); } reports.push(report); } } else { for (const reportPath of reportPaths) { if (reportPath.toString() === accumulatedProjectReportPath.toString()) { continue; // Skip the accumulated report itself } const report = profiler_core_1.ProjectReport.loadFromFile(reportPath, 'bin'); if (!report) { throw new Error(`ProjectReport could not be found: ${reportPath}`); } reports.push(report); } } const engineModule = reports.length > 0 ? reports[0].globalIndex.engineModule : profiler_core_1.NodeModule.currentEngineModule(); const globalIndex = new profiler_core_1.GlobalIndex(engineModule); const moduleIndex = globalIndex.getModuleIndex('upsert'); const accumulatedProjectReport = profiler_core_1.ProjectReport.merge(moduleIndex, ...reports); if (profiler !== undefined) { yield profiler.finish('latest'); profiler_core_1.LoggerHelper.success('Stored performance report at', profiler.exportAssetHelper.outputReportPath('latest').toPlatformString()); } if (options.output) { accumulatedProjectReport.storeToFile(new profiler_core_1.UnifiedPath(options.output), 'bin', profilerConfig); profiler_core_1.LoggerHelper.log(`The report was stored at ${options.output}`); } else { profiler_core_1.LoggerHelper.log('The report was not stored, because no output path was provided'); } accumulatedProjectReport.relativeRootDir = expectedAccumulatedReport.relativeRootDir; const reportsAreEqual = expectedAccumulatedReport.hash() === accumulatedProjectReport.hash(); if (reportsAreEqual) { profiler_core_1.LoggerHelper.success('The reports are equal'); } else { profiler_core_1.LoggerHelper.warn('The reports are not equal'); } }); } inspectCPUProfiles() { return __awaiter(this, void 0, void 0, function* () { const profilerConfig = profiler_core_1.ProfilerConfig.autoResolve(); const exportAssetHelper = new profiler_core_1.ExportAssetHelper(profilerConfig.getOutDir().join('jest')); const accumulatedProjectReportPath = exportAssetHelper.outputAccumulatedReportPath(); const reportPaths = exportAssetHelper.allReportPathsInOutputDir(); let totalNodeCount = 0, totalSourceNodeLocationCount = 0, totalSampleCount = 0, totalHits = 0, totalCPUTime = 0; for (const reportPath of reportPaths) { if (reportPath.toString() === accumulatedProjectReportPath.toString()) { continue; // Skip the accumulated report itself } const title = exportAssetHelper.titleFromReportFilePath(reportPath); const cpuProfilePath = exportAssetHelper.outputCPUProfilePath(title); const report = profiler_core_1.ProjectReport.loadFromFile(reportPath, 'bin'); if (!report) { profiler_core_1.LoggerHelper.error(`ProjectReport could not be found: ${reportPath}`); continue; } const cpuProfile = yield profiler_core_1.CPUProfileHelper.loadFromFile(cpuProfilePath); if (cpuProfile === undefined) { profiler_core_1.LoggerHelper.error(`CPU profile could not be loaded from ${cpuProfilePath.toPlatformString()}. ` + 'Please make sure the file exists and is a valid CPU profile.'); return; } const inspectResult = yield profiler_core_1.CPUProfileHelper.inspect(cpuProfile); totalNodeCount += inspectResult.nodeCount; totalSourceNodeLocationCount += inspectResult.sourceNodeLocationCount; totalSampleCount += inspectResult.sampleCount; totalHits += inspectResult.totalHits; totalCPUTime += inspectResult.totalCPUTime; const reportsTotal = report.totalAndMaxMetaData().total.sensorValues.aggregatedCPUTime; if (reportsTotal !== inspectResult.totalCPUTime) { profiler_core_1.LoggerHelper.warn(`Inconsistent CPU time in report: ${title}.\n` + `Profile CPU Time: ${inspectResult.totalCPUTime}\n` + `Report CPU Time: ${reportsTotal}`); } else { profiler_core_1.LoggerHelper.success(`Consistent CPU time in report: ${title}. CPU Time: ${reportsTotal}`); } } profiler_core_1.LoggerHelper.table([ { type: 'Files Inspected', value: reportPaths.length }, { type: 'Total Node Count', value: totalNodeCount }, { type: 'Source Node Location Count', value: totalSourceNodeLocationCount }, { type: 'Sample Count', value: totalSampleCount }, { type: 'Total Hits', value: totalHits }, { type: 'Total CPU Time', value: totalCPUTime, unit: 'µs' } ], ['type', 'value', 'unit']); }); } verifyTrees() { return __awaiter(this, void 0, void 0, function* () { const profilerConfig = profiler_core_1.ProfilerConfig.autoResolve(); const exportAssetHelper = new profiler_core_1.ExportAssetHelper(profilerConfig.getOutDir().join('jest')); const reportPaths = exportAssetHelper.allReportPathsInOutputDir(); let totalDiff = 0; for (const reportPath of reportPaths) { const projectReport = profiler_core_1.ProjectReport.loadFromFile(reportPath, 'bin'); if (!projectReport) { profiler_core_1.LoggerHelper.error(`ProjectReport could not be found: ${reportPath}`); continue; } const sourceFileMetaDataTree = profiler_core_1.SourceFileMetaDataTree.fromProjectReport(projectReport).filter(projectReport.asSourceNodeGraph(), undefined, undefined).node; if (!sourceFileMetaDataTree) { profiler_core_1.LoggerHelper.error(`SourceFileMetaDataTree could not be constructed from ProjectReport: ${reportPath}`); continue; } const total = projectReport.totalAndMaxMetaData().total; const treeSum = sourceFileMetaDataTree.aggregatedInternSourceMetaData.total.sensorValues .aggregatedCPUTime + sourceFileMetaDataTree.headlessSensorValues.langInternalCPUTime + sourceFileMetaDataTree.headlessSensorValues.externCPUTime; // const treeSum = sourceFileMetaDataTree.aggregatedInternSourceMetaData.total.sensorValues.selfCPUTime + // sourceFileMetaDataTree.aggregatedLangInternalSourceNodeMetaData.total.sensorValues.selfCPUTime + // sourceFileMetaDataTree.aggregatedExternSourceMetaData.total.sensorValues.selfCPUTime const diff = total.sensorValues.aggregatedCPUTime - treeSum; totalDiff += diff; if (diff !== 0) { profiler_core_1.LoggerHelper.error(`Inconsistent SourceFileMetaDataTree in report: ${reportPath.toPlatformString()}.\n` + 'Tree sum does not match total CPU time.\n', `Tree Sum: ${treeSum}`, `Total CPU Time: ${total.sensorValues.aggregatedCPUTime}`, `Difference: ${diff}`); continue; } } if (totalDiff === 0) { profiler_core_1.LoggerHelper.success('All SourceFileMetaDataTrees are consistent.'); } else { profiler_core_1.LoggerHelper.error(`Total CPU time difference across all reports: ${totalDiff}`); } }); } } exports.default = JestCommands; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSmVzdENvbW1hbmRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL0plc3RDb21tYW5kcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLHVDQUF3QjtBQUV4QixnREFBNEM7QUFDNUMsMERBYStCO0FBQy9CLHlDQUFtQztBQUVuQyxNQUFxQixZQUFZO0lBQ2hDO1FBQ0MsTUFBTSxXQUFXLEdBQUcsbUJBQU87YUFDekIsT0FBTyxDQUFDLE1BQU0sQ0FBQzthQUNmLFdBQVcsQ0FDWCwwRkFBMEYsQ0FDMUYsQ0FBQTtRQUVGLFdBQVc7YUFDVCxPQUFPLENBQUMsUUFBUSxDQUFDO2FBQ2pCLFdBQVcsQ0FDWCxvSEFBb0gsQ0FDcEg7YUFDQSxNQUFNLENBQ04sdUJBQXVCLEVBQ3ZCLGtGQUFrRixDQUNsRjthQUNBLE1BQU0sQ0FDTixZQUFZLEVBQ1osK0RBQStELENBQy9EO2FBQ0EsTUFBTSxDQUNOLGVBQWUsRUFDZixzSUFBc0ksQ0FDdEk7YUFDQSxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtRQUVoQyxXQUFXO2FBQ1QsT0FBTyxDQUFDLGtCQUFrQixDQUFDO2FBQzNCLFdBQVcsQ0FDWCxvR0FBb0csQ0FDcEc7YUFDQSxNQUFNLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFBO1FBRTVDLFdBQVc7YUFDVCxPQUFPLENBQUMsY0FBYyxDQUFDO2FBQ3ZCLFdBQVcsQ0FDWCx1RkFBdUYsQ0FDdkY7YUFDQSxNQUFNLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtJQUN0QyxDQUFDO0lBRUQsTUFBTSxDQUFDLElBQUk7UUFDVixPQUFPLElBQUksWUFBWSxFQUFFLENBQUE7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