Socket
Socket
Sign inDemoInstall

0x

Package Overview
Dependencies
Maintainers
3
Versions
123
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

0x - npm Package Compare versions

Comparing version 4.11.0 to 5.0.0

.github/workflows/ci.yml

4

docs/kernel-tracing.md

@@ -9,3 +9,3 @@ # 0x kernel tracing

Capturing these stacks requires kernel level tracing, which `0x` supports
on Linux and macOS.
on Linux.

@@ -37,3 +37,3 @@ ## Requirements

as bytecode handlers. In order to resolve this, the kernel tracing software
(perf and DTrace) has to be able to jump to another memory address where the name
(perf) has to be able to jump to another memory address where the name
of the JavaScript function is held, however the memory address that it the kernel

@@ -40,0 +40,0 @@ profiler should jump to is not exposed by the V8 engine to the kernel profiler.

'use strict'
const { sun, linux, windows, v8 } = require('./platform')
const { linux, v8 } = require('./platform')
const debug = require('debug')('0x')

@@ -46,3 +46,3 @@ const { join, isAbsolute, relative, dirname } = require('path')

args.title = args.title || `node ${args.argv.join(' ')}`
var { ticks, pid, folder, inlined } = await startProcessAndCollectTraceData(args)
const { ticks, pid, folder, inlined } = await startProcessAndCollectTraceData(args)

@@ -104,4 +104,4 @@ if (treeDebug === true) {

case 'linux': return linux(args, await isSudo())
case 'win32': return windows()
default: return sun(args, await isSudo())
default:
throw Error(`0x: ${platform} kernel tracing is not currently supported`)
}

@@ -139,3 +139,3 @@ }

var meta
let meta
try {

@@ -142,0 +142,0 @@ meta = JSON.parse(fs.readFileSync(join(folder, 'meta.json')))

@@ -52,3 +52,3 @@ 'use strict'

if (name === '-') return '-'
var htmlPath = (outputHtml || (
let htmlPath = (outputHtml || (
`{outputDir}${path.sep}{name}.html`

@@ -55,0 +55,0 @@ )).replace('{pid}', pid || 'UNKNOWN_PID')

@@ -9,4 +9,5 @@ 'use strict'

const preloadDirRx = RegExp(join(escape(__dirname), 'preload'))
// TODO(@rafaelgss): it looks like not working with v16 when node prefix was added
const internalModuleRegExp = /^.?(?:\(anonymous\)|internalBinding|NativeModule[^ ]*) [^/\\][a-zA-Z0-9_/\\-]+\.js:\d+:\d+$/
const nodeBootstrapRegExp = / internal\/bootstrap.+\.js:\d+:\d+$/
const nodeBootstrapRegExp = / (node:)?internal\/bootstrap.+(\.js)?:\d+(:?\d+)$/

@@ -16,3 +17,4 @@ module.exports = ticksToTree

function hasFileLocation (jsFrameName) {
return /:\d+:\d+$/.test(jsFrameName)
// perf samples doesn't contains the column number
return /:\d+(:\d+)?$/.test(jsFrameName)
}

@@ -63,8 +65,9 @@

// 4 inlinable optimized
var S = 0
let S = 0
if (type === 'JS') {
if (name[0] === ' ') name = '(anonymous)' + name
// TODO(@rafaelgss): adjust it for v16 + perf stacks
if (inlineInfo === true) {
const [ key ] = name.split(':')
const [key] = name.split(':')
const info = inlined[key]

@@ -91,3 +94,3 @@ if (info !== undefined) {

} else {
const [ lookup ] = stack[ix - 1] ? (stack[ix - 1].name.split(':')) : []
const [lookup] = stack[ix - 1] ? (stack[ix - 1].name.split(':')) : []
const callerMatch = inlined[key].find(({ caller }) => caller.key === lookup)

@@ -119,3 +122,3 @@ if (callerMatch) S += 2

function addToMergedTree (stack) {
var lastFrame = null
let lastFrame = null
stack.forEach((frame, ix) => {

@@ -142,3 +145,3 @@ // TODO need an elegant way to place inlined frames into *other* stacks

function addToUnmergedTree (stack) {
var lastFrame = null
let lastFrame = null
stack.forEach((frame, ix) => {

@@ -177,3 +180,3 @@ // TODO need an elegant way to place inlined frames into *other* stacks

let foundNodeModule = false
for (var i = frames.length - 1; i >= 0; i--) {
for (let i = frames.length - 1; i >= 0; i--) {
const frame = frames[i]

@@ -180,0 +183,0 @@ if (foundNodeModule) {

@@ -9,3 +9,3 @@ 'use strict'

const splitChar = '\n'
let lines = []
const lines = []
let searchStart = 0

@@ -35,3 +35,4 @@ let newlineIx

stacks[n].unshift({ name: line.trim().replace(/^LazyCompile:|Function:|Script:/, '') })
const name = cleanupStackLine(line)
stacks[n].unshift({ name, type: getStackType(name) })
return stacks

@@ -42,1 +43,31 @@ }, [])

}
function cleanupStackLine (line) {
// match linux perf stack trace
// eg: 7f587bf53533 Function:^emit node:events:340+0xa13 (/tmp/perf-132082.map)
const regexCallStack = line.match(/^\s*(\w+)\s*(.+) \((\S*)\)/)
if (regexCallStack) {
const rawfunc = regexCallStack[2]
// Linux 4.8 included symbol offsets in perf script output by default, eg:
// 7fffb84c9afc cpu_startup_entry+0x800047c022ec ([kernel.kallsyms])
// strip these off:
return rawfunc.replace(/\+0x[\da-f]+$/, '').replace(/^LazyCompile:|Function:|Script:/, '')
}
return line
}
function getStackType (name) {
if (name.match(/^RegExp:/)) {
return 'CODE:RegExp'
}
if (name.match(/::/)) {
return 'CPP'
}
if (name.match(/.*\.js:/) || name.match(/:/)) {
return 'JS'
}
// TODO(@rafaelgss): add native and kernel types
return 'SHARED_LIB'
}

@@ -70,3 +70,3 @@ 'use strict'

function isSudo () {
var check = spawn('sudo', ['-n', 'true'])
const check = spawn('sudo', ['-n', 'true'])
return new Promise((resolve, reject) => {

@@ -73,0 +73,0 @@ check.on('error', reject)

@@ -10,2 +10,4 @@ 'use strict'

const pump = require('pump')
const debug = require('debug')('0x')
const { Writable } = require('stream')

@@ -79,2 +81,4 @@ const readFile = promisify(fs.readFile)

debug(`v8LogToTicks isJson ${isJson}`)
debug(`v8LogToTicks isolateLogPath ${isolateLogPath}`)
const close = isJson ? () => {} : () => sp.kill()

@@ -94,4 +98,11 @@ const srcStream = isJson ? fs.createReadStream(isolateLogPath) : sp.stdout

}
}), process.stderr)
}), new Writable({
objectMode: true,
write (chunk, enc, cb) {
debug(`--prof-process --preprocces stderr: ${chunk.toString()}`)
cb()
}
}))
}
return new Promise((resolve, reject) => {

@@ -101,9 +112,6 @@ const ticks = []

const codeStream = parse('code.*', (code) => {
codes.push(code)
})
if (isJson === false) {
const v8Json = isolateLogPath.replace(extname(isolateLogPath), '.json')
pump(srcStream, fs.createWriteStream(v8Json), (err) => {
debug('v8LogToTicks pump srcStream -> fs.createWriteStream v8Json err', err)
if (err) {

@@ -116,3 +124,13 @@ reject(err)

pump(srcStream, codeStream, (err) => {
const codeStream = parse('code.*')
const codeDest = new Writable({
objectMode: true,
write (chunk, _, cb) {
codes.push(chunk)
cb()
}
})
pump(srcStream, codeStream, codeDest, (err) => {
debug('codeStream finished')
if (!err) return

@@ -129,15 +147,22 @@ if (/^Unexpected/.test(err.message)) {

const delayMs = options.collectDelay * 1000
const tickStream = parse('ticks.*', (tick) => {
const addr = tick.s.filter((n, i) => i % 2 === 0)
var stack = addr.map((n) => codes[n]).filter(Boolean)
if (firstTick.length === 0) {
firstTick.push(tick.tm)
const tickStream = parse('ticks.*')
const dest = new Writable({
objectMode: true,
write (tick, _, cb) {
const addr = tick.s.filter((n, i) => i % 2 === 0)
const stack = addr.map((n) => codes[n]).filter(Boolean)
if (firstTick.length === 0) {
firstTick.push(tick.tm)
}
// Compare ticks to first for collectDelay
if (tick.tm > (firstTick[0] + delayMs)) {
ticks.push(stack.reverse())
}
cb()
}
// Compare ticks to first for collectDelay
if (tick.tm > (firstTick[0] + delayMs)) {
ticks.push(stack.reverse())
}
})
pump(srcStream, tickStream, (err) => {
pump(srcStream, tickStream, dest, (err) => {
debug('tickStream finished')
if (err) {

@@ -152,2 +177,3 @@ close()

sp.on('exit', (code) => {
debug('v8LogToTicks sp.on exit code', code)
if (code !== 0) return reject(Error('v8 log conversion failed'))

@@ -154,0 +180,0 @@ })

'use strict'
const ajv = require('ajv')()
const Ajv = require('ajv')
const ajv = new Ajv()

@@ -5,0 +6,0 @@ module.exports = (schema) => {

{
"name": "0x",
"version": "4.11.0",
"version": "5.0.0",
"description": "🔥 single-command flamegraph profiling 🔥",

@@ -27,5 +27,5 @@ "main": "index.js",

"dependencies": {
"ajv": "^6.9.2",
"browserify": "^16.2.3",
"concat-stream": "^1.5.2",
"ajv": "^8.8.2",
"browserify": "^17.0.0",
"concat-stream": "^2.0.0",
"d3-fg": "^6.14.0",

@@ -36,8 +36,8 @@ "debounce": "^1.2.0",

"env-string": "^1.0.0",
"escape-string-regexp": "^1.0.5",
"escape-string-regexp": "^4.0.0",
"execspawn": "^1.0.1",
"has-unicode": "^2.0.1",
"hsl-to-rgb-for-reals": "^1.1.0",
"jsonstream2": "^1.1.2",
"make-dir": "^1.3.0",
"jsonstream2": "^3.0.0",
"make-dir": "^3.1.0",
"minimist": "^1.2.0",

@@ -48,20 +48,18 @@ "morphdom": "^2.3.3",

"opn": "^5.4.0",
"perf-sym": "^2.0.3",
"pump": "^3.0.0",
"pumpify": "^1.4.0",
"semver": "^5.5.1",
"pumpify": "^2.0.1",
"semver": "^7.3.5",
"single-line-log": "^1.0.1",
"split2": "^3.1.0",
"split2": "^4.0.0",
"tachyons": "^4.9.1",
"through2": "^2.0.5",
"which": "^1.2.4"
"through2": "^4.0.0",
"which": "^2.0.2"
},
"devDependencies": {
"esprima": "^4.0.1",
"rimraf": "^2.6.3",
"snazzy": "^8.0.0",
"standard": "^12.0.1",
"tap": "^12.5.3"
"rimraf": "^3.0.2",
"snazzy": "^9.0.0",
"standard": "^16.0.0",
"tap": "^15.0.0"
},
"browserify-shim": {}
}
'use strict'
const linux = require('./linux')
const sun = require('./sun')
const windows = require('./windows')
const v8 = require('./v8')
module.exports = { linux, sun, windows, v8 }
module.exports = { linux, v8 }

@@ -21,4 +21,7 @@ 'use strict'

const { status, outputDir, workingDir, name, onPort, pathToNodeBinary } = args
var perf = pathTo('perf')
if (!perf) return void cb(Error('Unable to locate perf - make sure it\'s in your PATH'))
const perf = pathTo('perf')
if (!perf) {
cb(Error('Unable to locate perf - make sure it\'s in your PATH'))
return
}
if (!sudo) {

@@ -30,7 +33,7 @@ status('Stacks are captured using perf(1), which requires sudo access\n')

var uid = parseInt(Math.random() * 1e9, 10).toString(36)
var perfdat = '/tmp/perf-' + uid + '.data'
var kernelTracingDebug = args.kernelTracingDebug
const uid = parseInt(Math.random() * 1e9, 10).toString(36)
const perfdat = '/tmp/perf-' + uid + '.data'
const kernelTracingDebug = args.kernelTracingDebug
var proc = spawn('sudo', [
const proc = spawn('sudo', [
'-E',

@@ -40,5 +43,3 @@ 'perf',

!kernelTracingDebug ? '-q' : '',
'-e',
'cpu-clock',
'-F 1000', // 1000 samples per sec === 1ms profiling like dtrace
'-F 99',
'-g',

@@ -61,6 +62,6 @@ '-o',

}
analyze(true)
filterInternalFunctions(perfdat)
})
var folder = getTargetFolder({ outputDir, workingDir, name, pid: proc.pid })
const folder = getTargetFolder({ outputDir, workingDir, name, pid: proc.pid })

@@ -81,3 +82,7 @@ if (onPort) status('Profiling\n')

process.once('SIGINT', analyze)
process.once('SIGINT', () => {
spawn('sudo', ['kill', '-SIGINT', '' + proc.pid], {
stdio: 'inherit'
})
})

@@ -99,3 +104,3 @@ function analyze (manual) {

function generate () {
var stacks = spawn('sudo', ['perf', 'script', '-i', perfdat], {
const stacks = spawn('sudo', ['perf', 'script', '-i', perfdat], {
stdio: [

@@ -112,11 +117,31 @@ 'ignore',

pid: proc.pid,
folder: folder
folder: folder,
inlined: []
})
})
}
}
spawn('sudo', ['kill', '-SIGINT', '' + proc.pid], {
stdio: 'inherit'
function filterInternalFunctions (perfFile) {
// Filtering out Node.js internal functions
const sed = spawn('sudo', [
'sed',
'-i',
'-e',
'/( __libc_start| LazyCompile | v8::internal::| Builtin:| Stub:| LoadIC:|[unknown]| LoadPolymorphicIC:)/d',
'-e',
's/ LazyCompile:[*~]?/ /',
perfFile
], {
stdio: ['ignore', 'inherit', 'inherit', 'ignore', 'ignore', 'pipe']
})
sed.on('exit', function (code) {
if (code !== null && code !== 0) {
console.error('`sed` subprocess error, code: ' + code)
return
}
analyze(true)
})
}
}

@@ -26,3 +26,3 @@ 'use strict'

let proc = spawn(pathToNodeBinary, [
const proc = spawn(pathToNodeBinary, [
'--prof',

@@ -147,3 +147,3 @@ `--logfile=${workingDir ? `${args.workingDir}/` : ''}%p-v8.log`,

let lastOptimizedFrame = null
let inlined = {}
const inlined = {}

@@ -159,3 +159,3 @@ // Try to parse an INLINE() item from the optimization log,

// would contain everything up to the ) in FUNCTION SOURCE ()
const [ match, inlinedFn ] = /INLINE \((.*?)\) id\{/.exec(s) || [ false ]
const [match, inlinedFn] = /INLINE \((.*?)\) id\{/.exec(s) || [false]
// shouldn't not match though..

@@ -162,0 +162,0 @@ if (match === false) return -1

@@ -16,3 +16,3 @@ # 0x

* Node v8.5.0 and above
* Node v12.x and above
* Default usage supports any Operating System that Node runs on!

@@ -22,12 +22,2 @@ * Chrome

## Legacy
Older versions of Node are supported via previous 0x versions:
| 0x | Node | macOS/SmartOS | Linux | Windows |
|----|------------|-------|-------|---------|
| v4 | v8.5.0+ | ☑️ | ☑️ | ☑️ |
| v3 | v6 – v8.4.0| ☑️ | ☑️ | ⤬ |
| v2 | v4 | ☑️ | ☑️ | ⤬ |
## Demo

@@ -202,6 +192,5 @@

Use an OS kernel tracing tool (perf on Linux or
dtrace on macOS and SmartOS). This will capture
Use an OS kernel tracing tool (perf on Linux). This will capture
native stack frames (C++ modules and Libuv I/O),
but may result in missing stacks on Node 8.
but may result in missing stacks from Node.js due to the optimizing compiler.

@@ -255,3 +244,3 @@ See [docs/kernel-tracing.md](docs/kernel-tracing.md) for more information.

Show output from DTrace or perf(1) tools.
Show output from perf(1) tools.

@@ -258,0 +247,0 @@ Default: false

@@ -81,3 +81,3 @@ 'use strict'

if (!state.control.optimized) return flamegraph.clear('yellow')
flamegraph.search(RegExp('^\\*'), 'yellow')
flamegraph.search(/^\\*/, 'yellow')
return

@@ -96,3 +96,3 @@ case 'not-optimized':

function zoom () {
var zoomLevel = 1
let zoomLevel = 1
return ({ type }) => {

@@ -99,0 +99,0 @@ switch (type) {

@@ -19,17 +19,23 @@ 'use strict'

const tiers = button(render)({ label: 'Tiers', pressed: state.tiers }, () => action({ type: 'tiers' }))
const view = state.renderMergedBtn && !state.visualizeCpuProfile ? button(render)({
label: state.merged ? 'Unmerge' : 'Merge',
width: '6.85em',
pressed: state.merged
}, () => action({ type: 'view' })) : ''
const optimized = state.visualizeCpuProfile ? '' : button(render)({
label: 'Optimized',
pressed: !state.merged && state.optimized,
disabled: state.merged
}, () => action({ type: 'optimized' }))
const unoptimized = state.visualizeCpuProfile ? '' : button(render)({
label: 'Unoptimized',
pressed: !state.merged && state.unoptimized,
disabled: state.merged
}, () => action({ type: 'not-optimized' }))
const view = state.renderMergedBtn && !state.visualizeCpuProfile
? button(render)({
label: state.merged ? 'Unmerge' : 'Merge',
width: '6.85em',
pressed: state.merged
}, () => action({ type: 'view' }))
: ''
const optimized = state.visualizeCpuProfile
? ''
: button(render)({
label: 'Optimized',
pressed: !state.merged && state.optimized,
disabled: state.merged
}, () => action({ type: 'optimized' }))
const unoptimized = state.visualizeCpuProfile
? ''
: button(render)({
label: 'Unoptimized',
pressed: !state.merged && state.unoptimized,
disabled: state.merged
}, () => action({ type: 'not-optimized' }))

@@ -36,0 +42,0 @@ return render`

@@ -27,3 +27,3 @@ 'use strict'

function v8cats (child) {
var name = child.name
const name = child.name
if (child.type) return { type: child.type }

@@ -30,0 +30,0 @@ // RegExp and Eval can contain anything (a method name defined in eval could be any string)

@@ -33,3 +33,3 @@ 'use strict'

}
const inlinable = renderInlinable ? hoc({ bg: bgs['inlinable'], exclude, name: 'inlinable', disabled: !enableInlinable }, action) : ''
const inlinable = renderInlinable ? hoc({ bg: bgs.inlinable, exclude, name: 'inlinable', disabled: !enableInlinable }, action) : ''
const native = hoc({ bg: bgs.native, exclude, name: 'native' }, action)

@@ -36,0 +36,0 @@ const regexp = hoc({ bg: bgs.regexp, exclude, name: 'regexp', lbl: 'rx' }, action)

@@ -45,5 +45,5 @@ 'use strict'

inlinable: `rgb(${hsl(
colors['inlinable'].h,
colors['inlinable'].s / 100 * 1.2,
colors['inlinable'].l / 100 * 1.2
colors.inlinable.h,
colors.inlinable.s / 100 * 1.2,
colors.inlinable.l / 100 * 1.2
)})`,

@@ -50,0 +50,0 @@ app: `rgb(${hsl(

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc