Socket
Socket
Sign inDemoInstall

@memlab/core

Package Overview
Dependencies
9
Maintainers
3
Versions
38
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.16 to 1.1.17

2

dist/lib/Config.d.ts

@@ -74,2 +74,3 @@ /**

webSourceDir: string;
debugDataDir: string;
runMetaFile: string;

@@ -94,2 +95,3 @@ snapshotSequenceFile: string;

metricsOutDir: string;
heapAnalysisLogDir: string;
reportScreenshotFile: string;

@@ -96,0 +98,0 @@ newUniqueClusterDir: string;

@@ -26,2 +26,3 @@ /**

private log;
private logFileSet;
private styles;

@@ -44,2 +45,4 @@ private static singleton;

private printStr;
registerLogFile(logFile: string): void;
unregisterLogFile(logFile: string): void;
beginSection(name: string): void;

@@ -46,0 +49,0 @@ endSection(name: string): void;

109

dist/lib/Console.js

@@ -17,2 +17,3 @@ /**

const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const readline_1 = __importDefault(require("readline"));

@@ -22,2 +23,3 @@ const string_width_1 = __importDefault(require("string-width"));

const TABLE_MAX_WIDTH = 50;
const LOG_BUFFER_LENGTH = 100;
const prevLine = '\x1b[F';

@@ -47,2 +49,12 @@ const eraseLine = '\x1b[K';

}
function registerExitCleanup(inst, exitHandler) {
const p = process;
// normal exit
p.on('exit', exitHandler.bind(null, { cleanup: true }));
// ctrl + c event
p.on('SIGINT', exitHandler.bind(null, { exit: true }));
// kill pid
p.on('SIGUSR1', exitHandler.bind(null, { exit: true }));
p.on('SIGUSR2', exitHandler.bind(null, { exit: true }));
}
class MemLabConsole {

@@ -52,2 +64,3 @@ constructor() {

this.log = [];
this.logFileSet = new Set();
this.styles = {

@@ -74,8 +87,11 @@ top: (msg) => msg,

MemLabConsole.singleton = inst;
// clean up output
const exitHandler = (
// eslint-disable-next-line @typescript-eslint/no-unused-vars
process.on('exit', (_code) => {
inst.flushLog();
_options,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_exitCode) => {
inst.flushLog({ sync: true });
inst.clearPrevOverwriteMsg();
});
};
registerExitCleanup(inst, exitHandler);
return inst;

@@ -106,24 +122,45 @@ }

// remove control characters
const rawMsg = msg
const lines = msg.split('\n').map(line => line
// eslint-disable-next-line no-control-regex
.replace(/[\u0000-\u001F\u007F-\u009F]/g, '')
.replace(/\[\d{1,3}m/g, '');
this.log.push(rawMsg);
if (this.log.length > 20) {
this.flushLog();
.replace(/\[\d{1,3}m/g, ''));
this.log.push(...lines);
if (this.log.length > LOG_BUFFER_LENGTH) {
this.flushLog({ sync: true });
}
}
flushLog() {
flushLog(options = {}) {
const str = this.log.join('\n');
if (str.length > 0) {
const file = this.config.consoleLogFile;
fs_1.default.appendFile(file, str + '\n', 'UTF-8', () => {
// NOOP
});
this.log = [];
if (str.length === 0) {
return;
}
this.log = [];
// synchronous logging
if (options.sync) {
for (const logFile of this.logFileSet) {
try {
fs_1.default.appendFileSync(logFile, str + '\n', 'UTF-8');
}
catch (_a) {
// fail silently
}
}
}
else {
// async logging
const emptyCallback = () => {
// no op
};
for (const logFile of this.logFileSet) {
try {
fs_1.default.appendFile(logFile, str + '\n', 'UTF-8', emptyCallback);
}
catch (_b) {
// fail silently
}
}
}
}
pushMsg(msg, options = {}) {
const len = this.sections.arr.length;
if (this.config.isContinuousTest || len === 0) {
if (this.sections.arr.length === 0) {
return;

@@ -149,3 +186,5 @@ }

}
stdout.write(eraseLine);
if (!this.config.muteConsole) {
stdout.write(eraseLine);
}
const msg = section.msgs.pop();

@@ -160,4 +199,6 @@ if (!msg) {

let n = line === 0 ? 1 : Math.ceil(line / width);
while (n-- > 0) {
stdout.write(prevLine + eraseLine);
if (!this.config.muteConsole && !this.config.isTest) {
while (n-- > 0) {
stdout.write(prevLine + eraseLine);
}
}

@@ -200,8 +241,18 @@ }

printStr(msg, options = {}) {
if (this.config.isTest || this.config.muteConsole) {
this.pushMsg(msg, options);
if (this.config.isTest) {
return;
}
console.log(msg);
this.pushMsg(msg, options);
if (this.config.isContinuousTest || !this.config.muteConsole) {
console.log(msg);
}
}
registerLogFile(logFile) {
this.flushLog({ sync: true });
this.logFileSet.add(path_1.default.resolve(logFile));
}
unregisterLogFile(logFile) {
this.flushLog({ sync: true });
this.logFileSet.delete(path_1.default.resolve(logFile));
}
beginSection(name) {

@@ -312,10 +363,12 @@ if (this.config.isContinuousTest) {

overwrite(msg, options = {}) {
const str = this.style(msg, options.level || 'low');
if (this.config.isContinuousTest) {
this.printStr(msg, { isOverwrite: false });
return;
}
if (this.config.isTest || this.config.muteConsole) {
this.printStr(str, { isOverwrite: true });
return;
}
if (this.config.isContinuousTest) {
return console.log(msg);
}
this.clearPrevOverwriteMsg();
const str = this.style(msg, options.level || 'low');
this.printStr(str, { isOverwrite: true });

@@ -322,0 +375,0 @@ }

@@ -35,4 +35,7 @@ /**

getWebSourceMetaFile(options?: FileOption): string;
getDebugDataDir(options?: FileOption): string;
getDebugSourceFile(options?: FileOption): string;
getPersistDataDir(options: FileOption): string;
getLoggerOutDir(options?: FileOption): string;
getHeapAnalysisLogDir(options?: FileOption): string;
getTraceClustersDir(options?: FileOption): string;

@@ -65,2 +68,3 @@ getTraceJSONDir(options?: FileOption): string;

};
initNewHeapAnalysisLogFile(options?: FileOption): string;
getAndInitTSCompileIntermediateDir(): string;

@@ -67,0 +71,0 @@ clearDataDirs(options?: FileOption): void;

@@ -101,2 +101,8 @@ "use strict";

}
getDebugDataDir(options = {}) {
return path_1.default.join(this.getDataBaseDir(options), 'debug');
}
getDebugSourceFile(options = {}) {
return path_1.default.join(this.getDebugDataDir(options), 'file.js');
}
getPersistDataDir(options) {

@@ -108,2 +114,6 @@ return path_1.default.join(this.getDataBaseDir(options), 'persist');

}
// all heap analysis results generated
getHeapAnalysisLogDir(options = {}) {
return path_1.default.join(this.getLoggerOutDir(options), 'heap-analysis');
}
// all trace clusters generated from the current run

@@ -205,2 +215,11 @@ getTraceClustersDir(options = {}) {

}
// create a unique log file created for heap analysis output
initNewHeapAnalysisLogFile(options = {}) {
const dir = this.getHeapAnalysisLogDir(options);
const file = path_1.default.join(dir, `analysis-${Utils_1.default.getUniqueID()}-out.log`);
if (!fs_extra_1.default.existsSync(file)) {
fs_extra_1.default.createFileSync(file);
}
return file;
}
getAndInitTSCompileIntermediateDir() {

@@ -218,2 +237,3 @@ const dir = path_1.default.join(this.getTmpDir(), 'memlab-code');

this.emptyDirIfExists(this.getWebSourceDir(options));
this.emptyDirIfExists(this.getDebugDataDir(options));
const dataSuffix = ['.heapsnapshot', '.json', '.png'];

@@ -245,2 +265,4 @@ const files = fs_extra_1.default.readdirSync(curDataDir);

this.emptyDirIfExists(this.getUniqueTraceClusterDir(options));
// all heap analysis results
this.emptyDirIfExists(this.getHeapAnalysisLogDir(options));
}

@@ -302,5 +324,8 @@ resetBrowserDir() {

config.webSourceDir = joinAndProcessDir(options, this.getWebSourceDir(options));
config.debugDataDir = joinAndProcessDir(options, this.getDebugDataDir(options));
config.dataBuilderDataDir = joinAndProcessDir(options, config.dataBaseDir, 'dataBuilder');
config.persistentDataDir = joinAndProcessDir(options, this.getPersistDataDir(options));
// register the default log file
config.consoleLogFile = path_1.default.join(config.curDataDir, 'console-log.txt');
Console_1.default.registerLogFile(config.consoleLogFile);
config.runMetaFile = this.getRunMetaFile(options);

@@ -323,2 +348,4 @@ config.snapshotSequenceFile = this.getSnapshotSequenceMetaFile(options);

config.traceJsonOutDir = joinAndProcessDir(options, this.getTraceJSONDir(options));
// heap analysis results
config.heapAnalysisLogDir = joinAndProcessDir(options, this.getHeapAnalysisLogDir(options));
config.metricsOutDir = joinAndProcessDir(options, loggerOutDir, 'metrics');

@@ -325,0 +352,0 @@ config.reportScreenshotFile = path_1.default.join(outDir, 'report.png');

@@ -260,6 +260,5 @@ /**

const locationIdx = heapSnapshot._nodeIdx2LocationIdx[this.idx];
if (locationIdx == null) {
return null;
}
return new HeapLocation_1.default(heapSnapshot, locationIdx);
return locationIdx == null
? null
: new HeapLocation_1.default(heapSnapshot, locationIdx);
}

@@ -266,0 +265,0 @@ // search reference by edge name and edge type

@@ -209,4 +209,4 @@ /**

while (locationIdx < this._locationCount) {
const id = locations[locationIdx * locationFieldsCount + this._locationObjectIndexOffset];
this._nodeIdx2LocationIdx[id] = locationIdx;
const nodeIndex = locations[locationIdx * locationFieldsCount + this._locationObjectIndexOffset];
this._nodeIdx2LocationIdx[nodeIndex] = locationIdx;
++locationIdx;

@@ -213,0 +213,0 @@ }

@@ -10,11 +10,6 @@ /**

*/
import type { E2EStepInfo, HeapNodeIdSet, IHeapNode, IHeapSnapshot, IMemoryAnalystOptions, IMemoryAnalystSnapshotDiff, LeakTracePathItem, Optional, IOveralLeakInfo, TraceCluster, ISerializedInfo } from './Types';
import type { E2EStepInfo, HeapNodeIdSet, IHeapSnapshot, IMemoryAnalystOptions, IMemoryAnalystSnapshotDiff, IOveralHeapInfo, LeakTracePathItem, Optional, IOveralLeakInfo, TraceCluster, ISerializedInfo } from './Types';
import TraceFinder from '../paths/TraceFinder';
declare class MemoryAnalyst {
checkLeak(): Promise<ISerializedInfo[]>;
checkUnbound(options?: IMemoryAnalystOptions): Promise<void>;
breakDownMemoryByShapes(options?: {
file?: string;
}): Promise<void>;
detectUnboundGrowth(options?: IMemoryAnalystOptions): Promise<void>;
detectMemoryLeaks(): Promise<ISerializedInfo[]>;

@@ -27,3 +22,2 @@ visualizeMemoryUsage(options?: IMemoryAnalystOptions): void;

diffSnapshots(loadAll?: boolean): Promise<IMemoryAnalystSnapshotDiff>;
private calculateRetainedSizes;
preparePathFinder(snapshot: IHeapSnapshot): TraceFinder;

@@ -33,10 +27,7 @@ private dumpPageInteractionSummary;

private filterLeakedObjects;
aggregateDominatorMetrics(ids: HeapNodeIdSet, snapshot: IHeapSnapshot, checkNodeCb: (node: IHeapNode) => boolean, nodeMetricsCb: (node: IHeapNode) => number): number;
private getOverallHeapInfo;
getRetainedSize(node: IHeapNode): number;
getOverallHeapInfo(snapshot: IHeapSnapshot, options?: {
force?: boolean;
}): Optional<IOveralHeapInfo>;
getOverallLeakInfo(leakedNodeIds: HeapNodeIdSet, snapshot: IHeapSnapshot): Optional<IOveralLeakInfo>;
private printHeapInfo;
private breakDownSnapshotByShapes;
private isTrivialEdgeForBreakDown;
private breakDownByReferrers;
printHeapInfo(leakInfo: IOveralHeapInfo): void;
private printHeapAndLeakInfo;

@@ -43,0 +34,0 @@ private logLeakTraceSummary;

@@ -26,3 +26,2 @@ /**

const babar_1 = __importDefault(require("babar"));
const chalk_1 = __importDefault(require("chalk"));
const LeakClusterLogger_1 = __importDefault(require("../logger/LeakClusterLogger"));

@@ -46,152 +45,2 @@ const LeakTraceDetailsLogger_1 = __importDefault(require("../logger/LeakTraceDetailsLogger"));

}
checkUnbound(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
this.visualizeMemoryUsage(options);
Utils_1.default.checkSnapshots(options);
yield this.detectUnboundGrowth(options);
});
}
breakDownMemoryByShapes(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const opt = { buildNodeIdIndex: true, verbose: true };
const file = options.file ||
Utils_1.default.getSnapshotFilePathWithTabType(/.*/) ||
'<EMPTY_FILE_PATH>';
const snapshot = yield Utils_1.default.getSnapshotFromFile(file, opt);
this.preparePathFinder(snapshot);
const heapInfo = this.getOverallHeapInfo(snapshot, { force: true });
if (heapInfo) {
this.printHeapInfo(heapInfo);
}
this.breakDownSnapshotByShapes(snapshot);
});
}
// find any objects that keeps growing
detectUnboundGrowth(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const nodeInfo = Object.create(null);
let hasCheckedFirstSnapshot = false;
let snapshot = null;
const isValidNode = (node) => node.type === 'object' ||
node.type === 'closure' ||
node.type === 'regexp';
const initNodeInfo = (node) => {
if (!isValidNode(node)) {
return;
}
const n = node.retainedSize;
nodeInfo[node.id] = {
type: node.type,
name: node.name,
min: n,
max: n,
history: [n],
node,
};
};
const updateNodeInfo = (node) => {
const item = nodeInfo[node.id];
if (!item) {
return;
}
if (node.name !== item.name || node.type !== item.type) {
nodeInfo[node.id] = null;
return;
}
const n = node.retainedSize;
// only monotonic increase?
if (Config_1.default.monotonicUnboundGrowthOnly && n < item.max) {
nodeInfo[node.id] = null;
return;
}
item.history.push(n);
item.max = Math.max(item.max, n);
item.min = Math.min(item.min, n);
};
// summarize the heap objects info in current heap snapshot
// this is mainly used for better understanding of the % of
// objects released and allocated over time
const maybeSummarizeNodeInfo = () => {
if (!Config_1.default.verbose) {
return;
}
let n = 0;
for (const k in nodeInfo) {
if (nodeInfo[k]) {
++n;
}
}
Console_1.default.lowLevel(`Objects tracked: ${n}`);
};
Console_1.default.overwrite('Checking unbounded objects...');
const snapshotFiles = options.snapshotDir
? // load snapshots from a directory
Utils_1.default.getSnapshotFilesInDir(options.snapshotDir)
: // load snapshots based on the visit sequence meta data
Utils_1.default.getSnapshotFilesFromTabsOrder();
for (const file of snapshotFiles) {
// force GC before loading each snapshot
if (global.gc) {
global.gc();
}
// load and preprocess heap snapshot
const opt = { buildNodeIdIndex: true, verbose: true };
snapshot = yield Utils_1.default.getSnapshotFromFile(file, opt);
this.calculateRetainedSizes(snapshot);
// keep track of heap objects
if (!hasCheckedFirstSnapshot) {
// record Ids in the snapshot
snapshot.nodes.forEach(initNodeInfo);
hasCheckedFirstSnapshot = true;
}
else {
snapshot.nodes.forEach(updateNodeInfo);
maybeSummarizeNodeInfo();
}
}
// exit if no heap snapshot found
if (!hasCheckedFirstSnapshot) {
return;
}
// post process and print the unbounded objects
const idsInLastSnapshot = new Set();
snapshot === null || snapshot === void 0 ? void 0 : snapshot.nodes.forEach(node => {
idsInLastSnapshot.add(node.id);
});
let ids = [];
for (const key in nodeInfo) {
const id = parseInt(key, 10);
const item = nodeInfo[id];
if (!item) {
continue;
}
if (!idsInLastSnapshot.has(id)) {
continue;
}
if (item.min === item.max) {
continue;
}
// filter out non-significant leaks
if (item.history[item.history.length - 1] < Config_1.default.unboundSizeThreshold) {
continue;
}
ids.push(Object.assign({ id }, item));
}
if (ids.length === 0) {
Console_1.default.midLevel('No increasing objects found.');
return;
}
ids = ids
.sort((o1, o2) => o2.history[o2.history.length - 1] - o1.history[o1.history.length - 1])
.slice(0, 20);
// print on terminal
const str = Serializer_1.default.summarizeUnboundedObjects(ids, { color: true });
Console_1.default.topLevel('Top growing objects in sizes:');
Console_1.default.lowLevel(' (Use `memlab trace --node-id=@ID` to get trace)');
Console_1.default.topLevel('\n' + str);
// save results to file
const csv = Serializer_1.default.summarizeUnboundedObjectsToCSV(ids);
fs_1.default.writeFileSync(Config_1.default.unboundObjectCSV, csv, 'UTF-8');
});
}
// find all unique pattern of leaks

@@ -373,7 +222,2 @@ detectMemoryLeaks() {

}
calculateRetainedSizes(snapshot) {
const finder = new TraceFinder_1.default();
// dominator and retained size
finder.calculateAllNodesRetainedSizes(snapshot);
}
// initialize the path finder

@@ -448,10 +292,2 @@ preparePathFinder(snapshot) {

}
aggregateDominatorMetrics(ids, snapshot, checkNodeCb, nodeMetricsCb) {
let ret = 0;
const dominators = Utils_1.default.getConditionalDominatorIds(ids, snapshot, checkNodeCb);
Utils_1.default.applyToNodes(dominators, snapshot, node => {
ret += nodeMetricsCb(node);
});
return ret;
}
getOverallHeapInfo(snapshot, options = {}) {

@@ -464,13 +300,10 @@ if (!Config_1.default.verbose && !options.force) {

const heapInfo = {
fiberNodeSize: this.aggregateDominatorMetrics(allIds, snapshot, Utils_1.default.isFiberNode, this.getRetainedSize),
regularFiberNodeSize: this.aggregateDominatorMetrics(allIds, snapshot, Utils_1.default.isRegularFiberNode, this.getRetainedSize),
detachedFiberNodeSize: this.aggregateDominatorMetrics(allIds, snapshot, Utils_1.default.isDetachedFiberNode, this.getRetainedSize),
alternateFiberNodeSize: this.aggregateDominatorMetrics(allIds, snapshot, Utils_1.default.isAlternateNode, this.getRetainedSize),
error: this.aggregateDominatorMetrics(allIds, snapshot, node => node.name === 'Error', this.getRetainedSize),
fiberNodeSize: Utils_1.default.aggregateDominatorMetrics(allIds, snapshot, Utils_1.default.isFiberNode, Utils_1.default.getRetainedSize),
regularFiberNodeSize: Utils_1.default.aggregateDominatorMetrics(allIds, snapshot, Utils_1.default.isRegularFiberNode, Utils_1.default.getRetainedSize),
detachedFiberNodeSize: Utils_1.default.aggregateDominatorMetrics(allIds, snapshot, Utils_1.default.isDetachedFiberNode, Utils_1.default.getRetainedSize),
alternateFiberNodeSize: Utils_1.default.aggregateDominatorMetrics(allIds, snapshot, Utils_1.default.isAlternateNode, Utils_1.default.getRetainedSize),
error: Utils_1.default.aggregateDominatorMetrics(allIds, snapshot, node => node.name === 'Error', Utils_1.default.getRetainedSize),
};
return heapInfo;
}
getRetainedSize(node) {
return node.retainedSize;
}
getOverallLeakInfo(leakedNodeIds, snapshot) {

@@ -480,3 +313,3 @@ if (!Config_1.default.verbose) {

}
const leakInfo = Object.assign(Object.assign({}, this.getOverallHeapInfo(snapshot)), { leakedSize: this.aggregateDominatorMetrics(leakedNodeIds, snapshot, () => true, this.getRetainedSize), leakedFiberNodeSize: this.aggregateDominatorMetrics(leakedNodeIds, snapshot, Utils_1.default.isFiberNode, this.getRetainedSize), leakedAlternateFiberNodeSize: this.aggregateDominatorMetrics(leakedNodeIds, snapshot, Utils_1.default.isAlternateNode, this.getRetainedSize) });
const leakInfo = Object.assign(Object.assign({}, this.getOverallHeapInfo(snapshot)), { leakedSize: Utils_1.default.aggregateDominatorMetrics(leakedNodeIds, snapshot, () => true, Utils_1.default.getRetainedSize), leakedFiberNodeSize: Utils_1.default.aggregateDominatorMetrics(leakedNodeIds, snapshot, Utils_1.default.isFiberNode, Utils_1.default.getRetainedSize), leakedAlternateFiberNodeSize: Utils_1.default.aggregateDominatorMetrics(leakedNodeIds, snapshot, Utils_1.default.isAlternateNode, Utils_1.default.getRetainedSize) });
return leakInfo;

@@ -494,107 +327,2 @@ }

}
breakDownSnapshotByShapes(snapshot) {
Console_1.default.overwrite('Breaking down memory by shapes...');
const breakdown = Object.create(null);
const population = Object.create(null);
// group objects based on their shapes
snapshot.nodes.forEach(node => {
if ((node.type !== 'object' && !Utils_1.default.isStringNode(node)) ||
Config_1.default.nodeIgnoreSetInShape.has(node.name)) {
return;
}
const key = Serializer_1.default.summarizeNodeShape(node);
breakdown[key] = breakdown[key] || new Set();
breakdown[key].add(node.id);
if (population[key] === undefined) {
population[key] = { examples: [], n: 0 };
}
++population[key].n;
// retain the top 5 examples
const examples = population[key].examples;
examples.push(node);
examples.sort((n1, n2) => n2.retainedSize - n1.retainedSize);
if (examples.length > 5) {
examples.pop();
}
});
// calculate and sort based on retained sizes
const ret = [];
for (const key in breakdown) {
const size = this.aggregateDominatorMetrics(breakdown[key], snapshot, () => true, this.getRetainedSize);
ret.push({ key, retainedSize: size });
}
ret.sort((o1, o2) => o2.retainedSize - o1.retainedSize);
Console_1.default.topLevel('Object shapes with top retained sizes:');
Console_1.default.lowLevel(' (Use `memlab trace --node-id=@ID` to get trace)\n');
const topList = ret.slice(0, 40);
// print settings
const opt = { color: true, compact: true };
const dot = chalk_1.default.grey('· ');
const colon = chalk_1.default.grey(': ');
// print the shapes with the biggest retained size
for (const o of topList) {
const referrerInfo = this.breakDownByReferrers(breakdown[o.key], snapshot);
const { examples, n } = population[o.key];
const shapeStr = Serializer_1.default.summarizeNodeShape(examples[0], opt);
const bytes = Utils_1.default.getReadableBytes(o.retainedSize);
const examplesStr = examples
.map(e => `@${e.id} [${Utils_1.default.getReadableBytes(e.retainedSize)}]`)
.join(' | ');
const meta = chalk_1.default.grey(` (N: ${n}, Examples: ${examplesStr})`);
Console_1.default.topLevel(`${dot}${shapeStr}${colon}${bytes}${meta}`);
Console_1.default.lowLevel(referrerInfo + '\n');
}
}
isTrivialEdgeForBreakDown(edge) {
const source = edge.fromNode;
return (source.type === 'array' ||
source.name === '(object elements)' ||
source.name === 'system' ||
edge.name_or_index === '__proto__' ||
edge.name_or_index === 'prototype');
}
breakDownByReferrers(ids, snapshot) {
const edgeNames = Object.create(null);
for (const id of ids) {
const node = snapshot.getNodeById(id);
for (const edge of (node === null || node === void 0 ? void 0 : node.referrers) || []) {
const source = edge.fromNode;
if (!Utils_1.default.isMeaningfulEdge(edge) ||
this.isTrivialEdgeForBreakDown(edge)) {
continue;
}
const sourceName = Serializer_1.default.summarizeNodeName(source, {
color: false,
});
const edgeName = Serializer_1.default.summarizeEdgeName(edge, {
color: false,
abstract: true,
});
const edgeKey = `[${sourceName}] --${edgeName}--> `;
edgeNames[edgeKey] = edgeNames[edgeKey] || {
numberOfEdgesToNode: 0,
source,
edge,
};
++edgeNames[edgeKey].numberOfEdgesToNode;
}
}
const referrerInfo = Object.entries(edgeNames)
.sort((i1, i2) => i2[1].numberOfEdgesToNode - i1[1].numberOfEdgesToNode)
.slice(0, 4)
.map(i => {
const meta = i[1];
const source = Serializer_1.default.summarizeNodeName(meta.source, {
color: true,
});
const edgeName = Serializer_1.default.summarizeEdgeName(meta.edge, {
color: true,
abstract: true,
});
const edgeSummary = `${source} --${edgeName}-->`;
return ` · ${edgeSummary}: ${meta.numberOfEdgesToNode}`;
})
.join('\n');
return referrerInfo;
}
printHeapAndLeakInfo(leakedNodeIds, snapshot) {

@@ -626,6 +354,2 @@ // write page interaction summary to the leaks text file

this.filterLeakedObjects(leakedNodeIds, snapshot);
if (Config_1.default.verbose) {
// show a breakdown of different object structures
this.breakDownSnapshotByShapes(snapshot);
}
const nodeIdInPaths = new Set();

@@ -653,3 +377,3 @@ const paths = [];

// cluster traces from the current run
const clusters = TraceBucket_1.default.clusterPaths(paths, snapshot, this.aggregateDominatorMetrics, {
const clusters = TraceBucket_1.default.clusterPaths(paths, snapshot, Utils_1.default.aggregateDominatorMetrics, {
strategy: Config_1.default.isMLClustering

@@ -663,3 +387,3 @@ ? new MLTraceSimilarityStrategy_1.default()

// cluster traces from the current run
const clustersUnclassified = TraceBucket_1.default.generateUnClassifiedClusters(paths, snapshot, this.aggregateDominatorMetrics);
const clustersUnclassified = TraceBucket_1.default.generateUnClassifiedClusters(paths, snapshot, Utils_1.default.aggregateDominatorMetrics);
LeakClusterLogger_1.default.logUnclassifiedClusters(clustersUnclassified);

@@ -695,3 +419,3 @@ }

// cluster traces from the current run
const clusters = TraceBucket_1.default.clusterPaths(paths, snapshot, this.aggregateDominatorMetrics, {
const clusters = TraceBucket_1.default.clusterPaths(paths, snapshot, Utils_1.default.aggregateDominatorMetrics, {
strategy: Config_1.default.isMLClustering

@@ -698,0 +422,0 @@ ? new MLTraceSimilarityStrategy_1.default()

@@ -10,3 +10,3 @@ /**

*/
import type { HaltOrThrowOptions } from './Types';
import type { HaltOrThrowOptions, HeapNodeIdSet, ShellOptions } from './Types';
import type { Browser, Page } from 'puppeteer';

@@ -126,3 +126,7 @@ import type { AnyAyncFunction, AnyOptions, E2EStepInfo, IHeapSnapshot, IHeapNode, IHeapEdge, IScenario, ILeakFilter, LeakTracePathItem, RunMetaInfo, RawHeapSnapshot, Nullable, Optional } from './Types';

declare function getClosureSourceUrl(node: IHeapNode): Nullable<string>;
export declare function runShell(command: string, options?: ShellOptions): string;
export declare function getRetainedSize(node: IHeapNode): number;
export declare function aggregateDominatorMetrics(ids: HeapNodeIdSet, snapshot: IHeapSnapshot, checkNodeCb: (node: IHeapNode) => boolean, nodeMetricsCb: (node: IHeapNode) => number): number;
declare const _default: {
aggregateDominatorMetrics: typeof aggregateDominatorMetrics;
applyToNodes: typeof applyToNodes;

@@ -153,2 +157,3 @@ callAsync: typeof callAsync;

getReadableTime: typeof getReadableTime;
getRetainedSize: typeof getRetainedSize;
getRunMetaFilePath: typeof getRunMetaFilePath;

@@ -221,2 +226,3 @@ getScenarioName: typeof getScenarioName;

resolveSnapshotFilePath: typeof resolveSnapshotFilePath;
runShell: typeof runShell;
setIsAlternateNode: typeof setIsAlternateNode;

@@ -223,0 +229,0 @@ setIsRegularFiberNode: typeof setIsRegularFiberNode;

@@ -47,5 +47,6 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveSnapshotFilePath = void 0;
exports.aggregateDominatorMetrics = exports.getRetainedSize = exports.runShell = exports.resolveSnapshotFilePath = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const child_process_1 = __importDefault(require("child_process"));
const process_1 = __importDefault(require("process"));

@@ -1755,3 +1756,48 @@ const Config_1 = __importStar(require("./Config"));

}
function runShell(command, options = {}) {
var _a, _b, _c;
const runningDir = (_b = (_a = options.dir) !== null && _a !== void 0 ? _a : Config_1.default.workDir) !== null && _b !== void 0 ? _b : FileManager_1.default.getTmpDir();
const execOptions = {
cwd: runningDir,
stdio: options.disconnectStdio
? []
: [process_1.default.stdin, process_1.default.stdout, process_1.default.stderr],
};
if (process_1.default.platform !== 'win32') {
execOptions.shell = '/bin/bash';
}
let ret = '';
try {
ret = child_process_1.default.execSync(command, execOptions);
}
catch (ex) {
if (Config_1.default.verbose) {
if (ex instanceof Error) {
Console_1.default.lowLevel(ex.message);
Console_1.default.lowLevel((_c = ex.stack) !== null && _c !== void 0 ? _c : '');
}
}
if (options.ignoreError === true) {
return '';
}
__1.utils.haltOrThrow(`Error when executing command: ${command}`);
}
return ret && ret.toString('UTF-8');
}
exports.runShell = runShell;
function getRetainedSize(node) {
return node.retainedSize;
}
exports.getRetainedSize = getRetainedSize;
function aggregateDominatorMetrics(ids, snapshot, checkNodeCb, nodeMetricsCb) {
let ret = 0;
const dominators = __1.utils.getConditionalDominatorIds(ids, snapshot, checkNodeCb);
__1.utils.applyToNodes(dominators, snapshot, node => {
ret += nodeMetricsCb(node);
});
return ret;
}
exports.aggregateDominatorMetrics = aggregateDominatorMetrics;
exports.default = {
aggregateDominatorMetrics,
applyToNodes,

@@ -1782,2 +1828,3 @@ callAsync,

getReadableTime,
getRetainedSize,
getRunMetaFilePath,

@@ -1850,2 +1897,3 @@ getScenarioName,

resolveSnapshotFilePath,
runShell,
setIsAlternateNode,

@@ -1852,0 +1900,0 @@ setIsRegularFiberNode,

{
"name": "@memlab/core",
"version": "1.1.16",
"version": "1.1.17",
"license": "MIT",

@@ -62,3 +62,3 @@ "description": "memlab core libraries",

"test-pkg": "jest .",
"publish-patch": "npm version patch --force && npm publish",
"publish-patch": "npm publish",
"clean-pkg": "rm -rf ./dist && rm -rf ./node_modules && rm -f ./tsconfig.tsbuildinfo"

@@ -65,0 +65,0 @@ },

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc