diff --git a/README.md b/README.md
index 10eaeb7..c9fe46d 100644
--- a/README.md
+++ b/README.md
@@ -37,14 +37,10 @@ If you record profiling information like so:
node --prof /path/to/my/script.js
-Then this will generate a bunch of `isolate*.log` files. Process one of them
-into usable JSON like so:
+Then this will generate one or more `isolate*.log` files. You can open
+the resulting profile in speedscope by running the following command:
- node --prof-process -preprocess -j isolate*.log > profile.v8log.json
-
-Then drop the resulting `profile.v8log.json` file into speedscope, or run
-
- speedscope profile.v8log.json
+ node --prof-process --preprocess -j isolate*.log | speedscope -
### Instruments.app
diff --git a/cli.js b/cli.js
index 0c7066d..884a273 100755
--- a/cli.js
+++ b/cli.js
@@ -2,73 +2,102 @@
const path = require('path')
const fs = require('fs')
const os = require('os')
+const stream = require('stream')
+
const opn = require('opn')
-const helpString = `
-Usage: speedscope [filepath]
+const helpString = `Usage: speedscope [filepath]
If invoked with no arguments, will open a local copy of speedscope in your default browser.
Once open, you can browse for a profile to import.
+
+If - is used as the filepath, will read from stdin instead.
+
+ cat /path/to/profile | speedscope -
`
-if (process.argv.includes('--help') || process.argv.includes('-h')) {
- console.log(helpString)
- process.exit(0)
+function getProfileStream(relPath) {
+ const absPath = path.resolve(process.cwd(), relPath)
+ if (relPath === '-') {
+ // Read from stdin
+ return process.stdin
+ } else {
+ return fs.createReadStream(absPath)
+ }
}
-if (process.argv.includes('--version') || process.argv.includes('-v')) {
- console.log('v' + require('./package.json').version)
- process.exit(0)
+function getProfileBuffer(relPath) {
+ const profileStream = getProfileStream(relPath)
+ const chunks = []
+ return new Promise((resolve, reject) => {
+ profileStream.pipe(
+ stream.Writable({
+ write(chunk, encoding, callback) {
+ chunks.push(chunk)
+ callback()
+ },
+ final() {
+ resolve(Buffer.concat(chunks))
+ },
+ }),
+ )
+ profileStream.on('error', ev => reject(ev))
+ })
}
-if (process.argv.length > 3) {
- console.log('At most one argument expected')
- console.log(helpString)
- process.exit(1)
+async function main() {
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
+ console.log(helpString)
+ return
+ }
+
+ if (process.argv.includes('--version') || process.argv.includes('-v')) {
+ console.log('v' + require('./package.json').version)
+ return
+ }
+
+ if (process.argv.length > 3) {
+ throw new Error('At most one argument expected')
+ }
+
+ let urlToOpen = 'file://' + path.resolve(__dirname, './dist/release/index.html')
+
+ if (process.argv.length === 3) {
+ const relPath = process.argv[2]
+ const sourceBuffer = await getProfileBuffer(relPath)
+ const filename = path.basename(relPath)
+ const sourceBase64 = sourceBuffer.toString('base64')
+ const jsSource = `speedscope.loadFileFromBase64(${JSON.stringify(filename)}, ${JSON.stringify(
+ sourceBase64,
+ )})`
+
+ const filePrefix = `speedscope-${+new Date()}-${process.pid}`
+ const jsPath = path.join(os.tmpdir(), `${filePrefix}.js`)
+ console.log(`Creating temp file ${jsPath}`)
+ fs.writeFileSync(jsPath, jsSource)
+ urlToOpen += `#localProfilePath=${jsPath}`
+
+ // For some silly reason, the OS X open command ignores any query parameters or hash parameters
+ // passed as part of the URL. To get around this weird issue, we'll create a local HTML file
+ // that just redirects.
+ const htmlPath = path.join(os.tmpdir(), `${filePrefix}.html`)
+ console.log(`Creating temp file ${htmlPath}`)
+ fs.writeFileSync(htmlPath, ``)
+
+ urlToOpen = `file://${htmlPath}`
+ }
+
+ console.log('Opening', urlToOpen, 'in your default browser')
+
+ await opn(urlToOpen, {wait: false})
}
-let urlToOpen = 'file://' + path.resolve(__dirname, './dist/release/index.html')
-
-if (process.argv.length === 3) {
- const absPath = path.resolve(process.cwd(), process.argv[2])
- let sourceBuffer
- try {
- sourceBuffer = fs.readFileSync(absPath)
- } catch (e) {
- console.log(e)
+main()
+ .then(() => {
+ process.exit(0)
+ })
+ .catch(e => {
+ console.log(e.stack + '\n')
console.log(helpString)
process.exit(1)
- }
- const filename = path.basename(absPath)
- const sourceBase64 = sourceBuffer.toString('base64')
- const jsSource = `speedscope.loadFileFromBase64(${JSON.stringify(filename)}, ${JSON.stringify(
- sourceBase64,
- )})`
-
- const filePrefix = `speedscope-${+new Date()}-${process.pid}`
- const jsPath = path.join(os.tmpdir(), `${filePrefix}.js`)
- console.log(`Creating temp file ${jsPath}`)
- fs.writeFileSync(jsPath, jsSource)
- urlToOpen += `#localProfilePath=${jsPath}`
-
- // For some silly reason, the OS X open command ignores any query parameters or hash parameters
- // passed as part of the URL. To get around this weird issue, we'll create a local HTML file
- // that just redirects.
- const htmlPath = path.join(os.tmpdir(), `${filePrefix}.html`)
- console.log(`Creating temp file ${htmlPath}`)
- fs.writeFileSync(htmlPath, ``)
-
- urlToOpen = `file://${htmlPath}`
-}
-
-console.log('Opening', urlToOpen, 'in your default browser')
-
-opn(urlToOpen, {wait: false}).then(
- () => {
- process.exit(0)
- },
- err => {
- console.error(err)
- console.exit(1)
- },
-)
+ })