'use strict'; const split = require("split"); const through2 = require("through2"); const path = require("path"); const rightPad = require("right-pad"); const defaultValue = require("default-value"); let unfound = {}; let found = {}; let foundBase = {}; let probablyNotReallyMissing = {}; let ceaseProcessing = false; let bannedPrefixes = [ "/dev/", "/sys/", "/run/" ] function isLibrary(name) { return (name.match(/\.so(?:\.[0-9]+)?$/) && !name.match(/\.so(?:\.[0-9]+)\.so$/)); } function getLibraryName(name) { let match = /^(.+)\.so(?:\.[0-9]+)?$/.exec(name); if (match == null) { throw new Error(`Library name regex failed for library: ${name}`); } else { return match[1]; } } process.stdin .pipe(split()) .pipe(through2.obj(function(item, enc, cb) { if (item.trim().length === 0) { cb(); } else { let match = /^\s*(?:\[pid\s*[0-9]+\] )?(?:open|stat)\("([^"]+)", ([A-Z_|]+|0x[0-9a-f]+|\{[^\}]+\})\) = (-?[0-9]+)(?: ([A-Z]+))?/.exec(item.toString()); if (match != null) { let path = match[1]; if (!bannedPrefixes.some(prefix => path.indexOf(prefix) === 0)) { this.push({ path: match[1], flags: match[2].split("|"), status: parseInt(match[3]), error: match[4] }); } } cb(); } })) .pipe(through2.obj(function(item, enc, cb) { let basename = path.basename(item.path); if (basename === "gdb") { ceaseProcessing = true; found["-- GDB ENCOUNTERED --"] = ""; unfound["-- GDB ENCOUNTERED --"] = ""; } if (ceaseProcessing === false) { if (item.status === -1) { unfound[basename] = item.path; } else { delete unfound[basename]; found[basename] = item.path; if (isLibrary(basename)) { foundBase[getLibraryName(basename)] = item.path; } } } cb(); })) .on("finish", () => { Object.keys(unfound).forEach((basename) => { function markNotMissing(reason) { delete unfound[basename]; probablyNotReallyMissing[basename] = reason; } if (basename.match(/\.dll.so$/) && found[basename.slice(0, -3)]) { markNotMissing("mono: is actually a DLL"); } else if (basename.match(/\.dll.so.la$/) && found[basename.slice(0, -6)]) { markNotMissing("mono: is actually a DLL"); } else if (basename.match(/\.res(?:G|S)?$/)) { markNotMissing("unity: .res / .resG / .resS file"); } else if (unfound[basename].match(/\/aot-cache\/[^\/]+\/[^\/]+$/)) { markNotMissing("mono: is an AOT cache path"); } else if (isLibrary(basename) && foundBase[getLibraryName(basename)]) { markNotMissing("other library version was already found") } }); function toArray(obj) { return Object.keys(obj).map((key) => { let value = obj[key]; return { basename: key, path: value } }); } let foundItems = toArray(found); let missingItems = toArray(unfound); let probablyNotReallyMissingItems = toArray(probablyNotReallyMissing); let missingLibraries = []; let missingOther = []; missingItems.forEach((item) => { if (isLibrary(item.basename)) { missingLibraries.push(item); } else { missingOther.push(item); } }); function printCategory(name, items, options = {}) { let maxWidth = Math.max.apply(null, items.map(item => item.basename.length)); let cutoff = defaultValue(options.cutoff, 30); let pathFormat = defaultValue(options.pathFormat, "%"); console.log(`\n${name}:`); if (items.length === 0) { console.log(" (none)"); } else { items.slice(0, cutoff).forEach((item) => { console.log(` ${rightPad(item.basename, maxWidth + 5)}${pathFormat.replace("%", item.path)}`); }); if (items.length > cutoff) { console.log(` (... ${items.length - cutoff} more ...)`); } } } printCategory("Found", foundItems); printCategory("Probably found", probablyNotReallyMissingItems, { pathFormat: "reason: %" }); printCategory("Missing miscellaneous", missingOther, { pathFormat: "(last attempted path: %)" }); printCategory("MISSING LIBRARIES", missingLibraries, { pathFormat: "(last attempted path: %)" }); }) .pipe(process.stdout);