Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinsawicki committed Feb 3, 2017
0 parents commit 73c0ab3
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cache
node_modules
2 changes: 2 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cache
test
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# electron-atos

Symbolicate an [Electron](http://electron.atom.io) macOS crash report that is
missing symbols.

## Usage

- Copy the lines missing symbols from a crash report to a local `crash.txt` file:

```
0 com.github.electron.framework 0x000000010d01fad3 0x10c497000 + 12094163
1 com.github.electron.framework 0x000000010d095014 0x10c497000 + 12574740
```

- Run `electron-atos` and specify the path to the file and the version of
Electron that was being used.

```sh
electron-atos --file /path/to/crash.txt --version 1.4.14
```

- The symbols of the given address will be printed out:

```
content::RenderProcessHostImpl::Cleanup() (in Electron Framework) (render_process_host_impl.cc:1908)
content::ServiceWorkerProcessManager::Shutdown() (in Electron Framework) (__tree:165)
```
13 changes: 13 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env node

const {argv} = require('yargs')
const atos = require('./index')

atos(argv, (error, symbols) => {
if (error != null) {
console.error(error.stack || error.message)
process.exit(1)
} else {
console.log(symbols.join('\n'))
}
})
102 changes: 102 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const ChildProcess = require('child_process')
const electronDownload = require('electron-download')
const extractZip = require('extract-zip')
const fs = require('fs')
const path = require('path')

module.exports = (options, callback) => {
const {version, quiet, file, force} = options
const directory = path.join(__dirname, 'cache', version)

const addresses = getAddresses(file)

download({version, quiet, directory, force}, (error) => {
if (error != null) return callback(error)

const symbols = []
const symbolicateNextAddress = () => {
const address = addresses.shift()
if (address == null) return callback(null, symbols)
symbolicate({directory, address}, (error, symbol) => {
if (error != null) return callback(error)
symbols.push(symbol)
symbolicateNextAddress()
})
}
symbolicateNextAddress()
})
}

const download = (options, callback) => {
const {version, quiet, directory, force} = options

if (fs.existsSync(directory) && !force) return callback()

electronDownload({
version: version,
dsym: true,
platform: 'darwin',
arch: 'x64',
quiet: quiet,
force: force
}, (error, zipPath) => {
if (error != null) return callback(error)
extractZip(zipPath, {dir: directory}, callback)
})
}

const symbolicate = (options, callback) => {
const {directory, address} = options

const command = 'atos'
const args = [
'-o',
getLibraryPath(directory, address.library),
'-l',
address.image
]
const atos = ChildProcess.spawn(command, args)
let output = ''
let error = ''
atos.on('close', (code) => {
if (code === 0) {
callback(null, output.trim())
} else {
error = `atos exited with ${code}: ${error}`
callback(new Error(error))
}
})
atos.stdout.on('data', (data) => {
output += data.toString()
})
atos.stderr.on('data', (data) => {
error += data.toString()
})
atos.stdin.write(address.address)
atos.stdin.end()
}

const getAddresses = (file) => {
const content = fs.readFileSync(file, 'utf8')
const addresses = []
content.split('\n').forEach((line) => {
const segments = line.split(/\s+/)
const index = parseInt(segments[0])
if (!isFinite(index)) return

const library = segments[1]
const address = segments[2]
const image = segments[3]
addresses.push({library, image, address})
})
return addresses
}

const getLibraryPath = (rootDirectory, library) => {
switch (library) {
case 'com.github.electron.framework':
return path.join(rootDirectory, 'Electron framework.framework.dSYM', 'Contents', 'Resources', 'DWARF', 'Electron Framework')
case 'libnode.dylib':
return path.join(rootDirectory, 'libnode.dylib.dSYM', 'Contents', 'Resources', 'DWARF', 'libnode.dylib')
}
}
32 changes: 32 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "electron-atos",
"version": "1.0.0",
"description": "Symbolicate Electron macOS crashes",
"main": "index.js",
"bin": {
"electron-atos": "./cli.js"
},
"scripts": {
"test": "mocha test"
},
"keywords": [
"atos",
"electron"
],
"author": "Kevin Sawicki",
"license": "ISC",
"dependencies": {
"extract-zip": "^1.6.0",
"yargs": "^6.6.0"
},
"os": [
"darwin"
],
"engines": {
"node": ">=7"
},
"devDependencies": {
"mocha": "^3.2.0",
"standard": "^8.6.0"
}
}
2 changes: 2 additions & 0 deletions test/fixtures/framework-addresses.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
0 com.github.electron.framework 0x000000010d01fad3 0x10c497000 + 12094163
1 com.github.electron.framework 0x000000010d095014 0x10c497000 + 12574740
2 changes: 2 additions & 0 deletions test/fixtures/framework-symbols.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
content::RenderProcessHostImpl::Cleanup() (in Electron Framework) (render_process_host_impl.cc:1908)
content::ServiceWorkerProcessManager::Shutdown() (in Electron Framework) (__tree:165)
2 changes: 2 additions & 0 deletions test/fixtures/node-addresses.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
3 libnode.dylib 0x000000010ab5c383 0x10aa09000 + 1389443
4 libnode.dylib 0x000000010ab678e9 0x10aa09000 + 1435881
2 changes: 2 additions & 0 deletions test/fixtures/node-symbols.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
worker (in libnode.dylib) (threadpool.c:76)
uv__thread_start (in libnode.dylib) (thread.c:54)
36 changes: 36 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const assert = require('assert')
const atos = require('..')
const fs = require('fs')
const path = require('path')

const {describe, it} = global

describe('atos', function () {
this.timeout(60000)

const fixtures = path.join(__dirname, 'fixtures')

it('returns an array of symbols for an Electron framework address', (done) => {
atos({
file: path.join(fixtures, 'framework-addresses.txt'),
version: '1.4.14'
}, (error, symbols) => {
if (error != null) return done(error)

assert.equal(symbols.join('\n'), fs.readFileSync(path.join(fixtures, 'framework-symbols.txt'), 'utf8').trim())
done()
})
})

it('returns an array of symbols for a node address', (done) => {
atos({
file: path.join(fixtures, 'node-addresses.txt'),
version: '1.4.14'
}, (error, symbols) => {
if (error != null) return done(error)

assert.equal(symbols.join('\n'), fs.readFileSync(path.join(fixtures, 'node-symbols.txt'), 'utf8').trim())
done()
})
})
})

0 comments on commit 73c0ab3

Please sign in to comment.