foxdriver
foxdriver copied to clipboard
Taking a heap snapshot
Hi,
I am trying to take a heap snapshot with foxdriver like so:
const Foxdriver = require('foxdriver')
;(async function() {
const { browser, tab } = await Foxdriver.attach('localhost', 6000)
await browser.tabs[0].memory.attach()
const id = await browser.tabs[0].memory.saveHeapSnapshot()
console.log(id)
const snapshot = await browser.heapSnapshotFile.getHeapSnapshot(id)
// console.log(snapshot)
browser.tabs[0].memory.detach()
browser.disconnect()
})()
However this seem to hang at line browser.heapSnapshotFile.getHeapSnapshot
What am I doing wrong?
I had taken a deeper look and found a workaround by adding a few properties and methods into the Client actor:
const Foxdriver = require('foxdriver')
const fs = require('fs')
;(async function() {
const { browser, tab } = await Foxdriver.attach('127.0.0.1', 6000)
// monkeypatch Client Actor to handle bulk data packets from firefox remote debugging protocol
function (client) {
client._bulk = new Map()
client.readMessage = function () {
var sep = this.incoming.toString().indexOf(':')
if (sep < 0) {
return false
}
/**
* beginning of a message is preceded by byteLength(message) + ":",
* or "bulk" + " " + actorId + " " + dataName + " " + byteLength(message) + ":"
*/
const messageHeaderString = this.incoming.slice(0, sep).toString()
const messageHeaders = messageHeaderString.split(' ')
const messageIsBulk = (messageHeaders[0] === 'bulk')
const messageActorId = messageHeaders[1]
const messageDataName = messageHeaders[2]
const byteLength = messageIsBulk ? parseInt(messageHeaders[3]) : parseInt(messageHeaderString)
/**
* check if response is complete
*/
if (this.incoming.length - (sep + 1) < byteLength) {
return false
}
this.incoming = this.incoming.slice(sep + 1)
const packet = this.incoming.slice(0, byteLength)
this.incoming = this.incoming.slice(byteLength)
if (messageIsBulk) {
// console.log(packet)
const bulkId = `bulk ${messageActorId} ${messageDataName}`
this.handleBulkPacket(packet, bulkId)
// this.handleMessage(packet)
} else {
this.handleMessage(packet)
}
return true
}
/**
* handle unsolicited bulk packet from server
*/
client.handleBulkPacket = function (packet, bulkId) {
this.log.info(`bulk packet: ${packet}`)
this.pushToBulkStack(bulkId, packet)
return this.emit('message', packet)
}
client.pushToBulkStack = function (bulkId, data) {
if (!this._bulk.has(bulkId)) {
// create array for the stack if it is does not already exists
this._bulk.set(bulkId, [])
}
this._bulk.get(bulkId).push(data)
}
client.popBulkStack = function (bulkId) {
return this._bulk.get(bulkId).pop()
}
} (browser.client)
// get snapshot
await browser.tabs[0].memory.attach()
const id = await browser.tabs[0].memory.saveHeapSnapshot()
let snapshotResponse = await browser.heapSnapshotFile.getHeapSnapshot(id)
// the actual snapshot data are available from the bulk stack after this point
let bulkId = `bulk ${snapshotResponse.from} heap-snapshot`
let snapshotData = browser.client.popBulkStack(bulkId)
// save snapshot to disk
fs.writeFile(id + '.fxsnapshot', snapshotData)
})()
I have found that getHeapSnapshot() uses Client Actor to send a transferSnapshot request to firefox, but when the response comes back, it is delivered as a bulk data packet followed by a JSON packet (see stream transport, packets here: https://docs.firefox-dev.tools/backend/protocol.html) but Client.readMessage() reads all incoming packets as JSON packets and stucks in an infinite loop when it gets a bulk data packet
The code above changes readMessage to both read the incoming packet and stores it into a local _bulk stack if it is a bulk data packet, which can be retrieved later from the stack with the actor ID