Skip to content

Commit e8c0afd

Browse files
authored
Attempt to use contents of /usr/bin/ldd file for greater performance (#19)
1 parent d49b8d3 commit e8c0afd

File tree

9 files changed

+575
-16
lines changed

9 files changed

+575
-16
lines changed

benchmark/call-familySync.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const performance = require('perf_hooks').performance;
2+
const libc = require('..');
3+
4+
const now = performance.now();
5+
libc.familySync();
6+
7+
console.log(`[family] Time Spent ${performance.now() - now}ms`);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const performance = require('perf_hooks').performance;
2+
const libc = require('..');
3+
4+
const now = performance.now();
5+
libc.isNonGlibcLinuxSync();
6+
7+
console.log(`[isNonGlibcLinux] Time Spent ${performance.now() - now}ms`);

benchmark/call-versionSync.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const performance = require('perf_hooks').performance;
2+
const libc = require('..');
3+
4+
const now = performance.now();
5+
libc.versionSync();
6+
7+
console.log(`[versionSync] Time Spent ${performance.now() - now}ms`);

benchmark/detect-libc.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const Benchmark = require('benchmark');
2+
const suite = new Benchmark.Suite();
3+
const libc = require('../');
4+
5+
suite.add('family', async function () {
6+
await libc.family();
7+
});
8+
9+
suite.add('familySync', function () {
10+
libc.familySync();
11+
});
12+
13+
suite.add('version', async function () {
14+
await libc.version();
15+
});
16+
17+
suite.add('versionSync', function () {
18+
libc.versionSync();
19+
});
20+
21+
suite.add('isNonGlibcLinux', async function () {
22+
await libc.isNonGlibcLinux();
23+
});
24+
25+
suite.add('isNonGlibcLinuxSync', function () {
26+
libc.isNonGlibcLinuxSync();
27+
});
28+
29+
suite
30+
// add listeners
31+
.on('cycle', function (event) {
32+
console.log(String(event.target));
33+
})
34+
.on('complete', function () {
35+
console.log('Fastest operation is ' + this.filter('fastest').map('name'));
36+
})
37+
.run({
38+
async: true
39+
});

lib/detect-libc.js

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
const childProcess = require('child_process');
77
const { isLinux, getReport } = require('./process');
8+
const { LDD_PATH, readFile, readFileSync } = require('./filesystem');
9+
10+
let cachedFamilyFilesystem;
11+
let cachedVersionFilesystem;
812

913
const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true';
1014
let commandOut = '';
@@ -39,13 +43,31 @@ const safeCommandSync = () => {
3943
*/
4044
const GLIBC = 'glibc';
4145

46+
/**
47+
* A Regexp constant to get the GLIBC Version.
48+
* @type {string}
49+
*/
50+
const RE_GLIBC_VERSION = /GLIBC\s(\d+\.\d+)/;
51+
4252
/**
4353
* A String constant containing the value `musl`.
4454
* @type {string}
4555
* @public
4656
*/
4757
const MUSL = 'musl';
4858

59+
/**
60+
* This string is used to find if the {@link LDD_PATH} is GLIBC
61+
* @type {string}
62+
*/
63+
const GLIBC_ON_LDD = GLIBC.toUpperCase();
64+
65+
/**
66+
* This string is used to find if the {@link LDD_PATH} is musl
67+
* @type {string}
68+
*/
69+
const MUSL_ON_LDD = MUSL.toLowerCase();
70+
4971
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-');
5072

5173
const familyFromReport = () => {
@@ -72,14 +94,51 @@ const familyFromCommand = (out) => {
7294
return null;
7395
};
7496

97+
const getFamilyFromLddContent = (content) => {
98+
if (content.includes(MUSL_ON_LDD)) {
99+
return MUSL;
100+
}
101+
if (content.includes(GLIBC_ON_LDD)) {
102+
return GLIBC;
103+
}
104+
return null;
105+
};
106+
107+
const familyFromFilesystem = async () => {
108+
if (cachedFamilyFilesystem !== undefined) {
109+
return cachedFamilyFilesystem;
110+
}
111+
cachedFamilyFilesystem = null;
112+
try {
113+
const lddContent = await readFile(LDD_PATH);
114+
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
115+
} catch (e) {}
116+
return cachedFamilyFilesystem;
117+
};
118+
119+
const familyFromFilesystemSync = () => {
120+
if (cachedFamilyFilesystem !== undefined) {
121+
return cachedFamilyFilesystem;
122+
}
123+
cachedFamilyFilesystem = null;
124+
try {
125+
const lddContent = readFileSync(LDD_PATH);
126+
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
127+
} catch (e) {}
128+
return cachedFamilyFilesystem;
129+
};
130+
75131
/**
76132
* Resolves with the libc family when it can be determined, `null` otherwise.
77133
* @returns {Promise<?string>}
78134
*/
79135
const family = async () => {
80136
let family = null;
81137
if (isLinux()) {
82-
family = familyFromReport();
138+
family = await familyFromFilesystem();
139+
if (!family) {
140+
family = familyFromReport();
141+
}
83142
if (!family) {
84143
const out = await safeCommand();
85144
family = familyFromCommand(out);
@@ -95,7 +154,10 @@ const family = async () => {
95154
const familySync = () => {
96155
let family = null;
97156
if (isLinux()) {
98-
family = familyFromReport();
157+
family = familyFromFilesystemSync();
158+
if (!family) {
159+
family = familyFromReport();
160+
}
99161
if (!family) {
100162
const out = safeCommandSync();
101163
family = familyFromCommand(out);
@@ -116,6 +178,36 @@ const isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC;
116178
*/
117179
const isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC;
118180

181+
const versionFromFilesystem = async () => {
182+
if (cachedVersionFilesystem !== undefined) {
183+
return cachedVersionFilesystem;
184+
}
185+
cachedVersionFilesystem = null;
186+
try {
187+
const lddContent = await readFile(LDD_PATH);
188+
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
189+
if (versionMatch) {
190+
cachedVersionFilesystem = versionMatch[1];
191+
}
192+
} catch (e) {}
193+
return cachedVersionFilesystem;
194+
};
195+
196+
const versionFromFilesystemSync = () => {
197+
if (cachedVersionFilesystem !== undefined) {
198+
return cachedVersionFilesystem;
199+
}
200+
cachedVersionFilesystem = null;
201+
try {
202+
const lddContent = readFileSync(LDD_PATH);
203+
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
204+
if (versionMatch) {
205+
cachedVersionFilesystem = versionMatch[1];
206+
}
207+
} catch (e) {}
208+
return cachedVersionFilesystem;
209+
};
210+
119211
const versionFromReport = () => {
120212
const report = getReport();
121213
if (report.header && report.header.glibcVersionRuntime) {
@@ -144,7 +236,10 @@ const versionFromCommand = (out) => {
144236
const version = async () => {
145237
let version = null;
146238
if (isLinux()) {
147-
version = versionFromReport();
239+
version = await versionFromFilesystem();
240+
if (!version) {
241+
version = versionFromReport();
242+
}
148243
if (!version) {
149244
const out = await safeCommand();
150245
version = versionFromCommand(out);
@@ -160,7 +255,10 @@ const version = async () => {
160255
const versionSync = () => {
161256
let version = null;
162257
if (isLinux()) {
163-
version = versionFromReport();
258+
version = versionFromFilesystemSync();
259+
if (!version) {
260+
version = versionFromReport();
261+
}
164262
if (!version) {
165263
const out = safeCommandSync();
166264
version = versionFromCommand(out);

lib/filesystem.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const fs = require('fs');
2+
3+
/**
4+
* The path where we can find the ldd
5+
*/
6+
const LDD_PATH = '/usr/bin/ldd';
7+
8+
/**
9+
* Read the content of a file synchronous
10+
*
11+
* @param {string} path
12+
* @returns {string}
13+
*/
14+
const readFileSync = path => fs.readFileSync(path, 'utf-8');
15+
16+
/**
17+
* Read the content of a file
18+
*
19+
* @param {string} path
20+
* @returns {Promise<string>}
21+
*/
22+
const readFile = path => new Promise((resolve, reject) => {
23+
fs.readFile(path, 'utf-8', (err, data) => {
24+
if (err) {
25+
reject(err);
26+
} else {
27+
resolve(data);
28+
}
29+
});
30+
});
31+
32+
module.exports = {
33+
LDD_PATH,
34+
readFileSync,
35+
readFile
36+
};

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"index.d.ts"
99
],
1010
"scripts": {
11-
"test": "semistandard && nyc --reporter=lcov --check-coverage --branches=100 ava test/unit.js"
11+
"test": "semistandard && nyc --reporter=lcov --check-coverage --branches=100 ava test/unit.js",
12+
"bench": "node benchmark/detect-libc",
13+
"bench:calls": "node benchmark/call-familySync.js && sleep 1 && node benchmark/call-isNonGlibcLinuxSync.js && sleep 1 && node benchmark/call-versionSync.js"
1214
},
1315
"repository": {
1416
"type": "git",
@@ -26,6 +28,7 @@
2628
"license": "Apache-2.0",
2729
"devDependencies": {
2830
"ava": "^2.4.0",
31+
"benchmark": "^2.1.4",
2932
"nyc": "^15.1.0",
3033
"proxyquire": "^2.1.3",
3134
"semistandard": "^14.2.3"

test/fixtexture-file.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1

0 commit comments

Comments
 (0)