Skip to content

Instantly share code, notes, and snippets.

@nojvek
Created February 18, 2016 22:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nojvek/1213471a84775421058e to your computer and use it in GitHub Desktop.
Save nojvek/1213471a84775421058e to your computer and use it in GitHub Desktop.
fs = require 'fs'
readline = require 'readline'
path = require 'path'
c = console
###
# Helpers
###
parseCliForPath = ->
# parse cli args
argv = process.argv
if argv.length < 3
c.log "Usage: coffee chromeProfileConverter.coffee trace.csv"
process.exit(1)
traceCSVFilePath = argv[2]
if not fs.existsSync(traceCSVFilePath)
c.log traceCSVFilePath, "doesn't exist"
process.exit(1)
return traceCSVFilePath
parseCSV = (traceCSVFilePath) ->
objects = []
lines = fs.readFileSync(traceCSVFilePath, 'utf-8').trim().split(/\r?\n/).map((line) -> line.split(", "))
headers = lines[0].map (str) -> str.toLowerCase().replace(/\s+|#/g, "")
for i in [1...lines.length]
object = {}
for key, j in headers
object[key] = lines[i][j]
if key is "stack" #divide stack into
object["callStack"] = object[key].split("/")
objects.push object
return objects
filterSamples = (samples) ->
#samples = samples.splice(0,1000)
filteredSamples = []
dllUsageMap = {js: 0}
for sample in samples
# filter out "n/a"
if sample.stack is "n/a"
continue
filteredCallStack = []
# WPA uses / for both paths and stack separators :()
# use state machine to parse out proper script paths
seenScriptDll = false
scriptCall = ""
for call in sample.callStack
# leave only js behind
match = call.match(/^(\w+\.)(dll|sys|exe)/i)
if match
dllName = match[0]
if not dllUsageMap[dllName]
dllUsageMap[dllName] = 1
else
dllUsageMap[dllName]++
if dllName is "F12Script2.dll"
seenScriptDll = true
else
if call is "[Root]"
filteredCallStack.push(call)
if seenScriptDll
scriptCall += "/" + call
if call.match(/\.js!/)
seenScriptDll = false
if not seenScriptDll and scriptCall
#c.log scriptCall
dllUsageMap.js++
filteredCallStack.push(scriptCall)
scriptCall = ""
sample.callStack = filteredCallStack
delete sample.stack
filteredSamples.push(sample)
# show dll usage map
dllTuples = []
for dllName,usageCount of dllUsageMap
dllTuples.push([dllName, usageCount])
dllTuples = dllTuples.sort((a, b) -> b[1] - a[1])
c.log "DLL Usage\n", dllTuples
return filteredSamples
convertToChromeCpuProfile = (samples) ->
sampleIdCount = 1
callPathNodeMap = {}
timestamps = []
sampleNodeIds = []
parseCall = (node) ->
call = node.functionName
match = call.match(/^(.*)!(.*)\-(\d+):(\d+)/)
if match
node.url = match[1]
node.functionName = match[2]
node.lineNumber = match[3]
node.columnNumber = match[4]
getProfileNode = (callStack) ->
callPath = ""
#callStack = callStack.slice(0,10)
stackLen = callStack.length
for call,i in callStack
parentCallPath = callPath
callPath += "/" + call
if callPath of callPathNodeMap
node = callPathNodeMap[callPath]
else
node =
id: sampleIdCount++
hitCount: 0
functionName: call
url: ""
lineNumber: 0
columnNumber: 0
children:[]
parseCall(node)
callPathNodeMap[callPath] = node
if parentCallPath
parentNode = callPathNodeMap[parentCallPath]
parentNode.children.push(node)
# increase hitCount if at the tip of callStack
if (i + 1) == stackLen
node.hitCount++
return node
for sample in samples
timestamps.push(sample.timestamp * 1000000) #seconds
node = getProfileNode(sample.callStack)
sampleNodeIds.push(node.id)
chromeCpuProfile =
head: callPathNodeMap["/[Root]"]
samples: sampleNodeIds
timestamps: timestamps
#c.log callPathNodeMap
return chromeCpuProfile
###
# Main
###
traceCSVFilePath = parseCliForPath()
# change outputFilePath extension
outputFilePath = path.parse(traceCSVFilePath)
outputFilePath.ext = ".cpuprofile"
outputFilePath = outputFilePath.dir + "/" + outputFilePath.name + outputFilePath.ext
samples = parseCSV(traceCSVFilePath)
c.log "Parsed", samples.length, "samples"
samples = filterSamples(samples.splice(0,samples.length))
c.log "Filtered samples", samples.length
#process.exit()
c.log "Generating profile tree"
chromeCpuProfile = convertToChromeCpuProfile(samples)
c.log "Creating json str"
#jsonStr = JSON.stringify(chromeCpuProfile)
jsonStr = JSON.stringify(chromeCpuProfile, null, '\t')
c.log "Writing to", outputFilePath
fs.writeFileSync(outputFilePath, jsonStr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment