-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.js
146 lines (126 loc) · 3.96 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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)
if (address.line != null) {
symbols.push(address.line)
symbolicateNextAddress()
} else {
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) => {
if (line.match(/load address/)) {
addresses.push(parseSamplingAddress(line))
} else {
addresses.push(parseAddress(line))
}
})
return addresses
}
// Lines from stack traces are of the format:
// 0 com.github.electron.framework 0x000000010d01fad3 0x10c497000 + 12094163
const parseAddress = (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]
// images are of the format: 0x10eb25000
if (/0x[0-9a-fA-F]+/.test(image)) {
return {library, image, address}
} else {
return {line: segments.slice(3).join(' ')}
}
}
// Lines from macOS sampling reports are of the format:
// 2189 ??? (in Electron Framework) load address 0x1052bd000 + 0x3f8e36 [0x1056b5e36]
const parseSamplingAddress = (line) => {
const isElectron = line.match(/\(in Electron Framework\)/)
const isNode = line.match(/\(in libnode\.dylib\)/)
if (!isElectron && !isNode) return
const addressMatch = line.match(/\[(0x[0-9a-fA-F]+)]/)
if (!addressMatch) return
const imageMatch = line.match(/(0x[0-9a-fA-F]+)/)
if (!imageMatch) return
const library = isElectron
? 'com.github.electron.framework'
: 'libnode.dylib'
const address = addressMatch[1]
const image = imageMatch[1]
return {library, image, address}
}
const getLibraryPath = (rootDirectory, library) => {
switch (library) {
case 'libnode.dylib':
return path.join(rootDirectory, 'libnode.dylib.dSYM', 'Contents', 'Resources', 'DWARF', 'libnode.dylib')
default:
return path.join(rootDirectory, 'Electron framework.framework.dSYM', 'Contents', 'Resources', 'DWARF', 'Electron Framework')
}
}