Skip to content

Instantly share code, notes, and snippets.

@jclulow
Created May 4, 2015 21:11
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 jclulow/9aa76671a258f724f4e0 to your computer and use it in GitHub Desktop.
Save jclulow/9aa76671a258f724f4e0 to your computer and use it in GitHub Desktop.
/*
* This utility uses "mdb -k" to dump out the KVM ring buffer
* for a particular VM. You must run it _BEFORE_ you kill or
* restart the qemu process. Output is to stdout, so direct
* that to a file -- it will be about ~500KB of JSON for a large
* VM.
*
* To run this:
*
* on SDC 6.X:
* /usr/bin/node dump_ring_buffer.js $QEMU_PID \
* > out.json
*
* on SDC 7+:
* /usr/node/bin/node dump_ring_buffer.js $QEMU_PID \
* > out.json
*
*/
var mod_child_process = require('child_process');
var mod_util = require('util');
var mod_crypto = require('crypto');
var mod_events = require('events');
var START_MARKER = '## 8b90d457-479d-4619-9b75-8407edb83cc5';
var END_MARKER = '## 2c37852b-3639-4298-ab18-1d4c6a5aa344';
var MARKER_REGEX = new RegExp(START_MARKER + ' ?\\n((?:.|[\\r\\n])*)' +
END_MARKER + ' ?\\n', 'm');
function
MDB(inputfile)
{
var self = this;
mod_events.EventEmitter.call(self);
self._exit = null;
self._qq = null;
self._q = [];
self._inputfile = inputfile;
self._proc = mod_child_process.spawn('/usr/bin/mdb',
[ inputfile ]);
self._data = '';
self._proc.stdout.on('data', function (ch) {
self._data += ch.toString('utf8');
self._handle();
});
self._proc.stderr.on('data', function (ch) {
self.emit('stderr', ch);
});
self._proc.once('exit', function(code, signal) {
self._exit = { code: code, signal: signal };
if (self._qq === null && self._q.length === 0)
self.emit('exit', code, signal);
});
/*
* Send a dummy command to separate any Banner noise
* from actual output:
*/
self.command(null, null, function () {
self._started = true;
});
}
mod_util.inherits(MDB, mod_events.EventEmitter);
MDB.prototype._dispatch = function
_dispatch()
{
var self = this;
if (self._qq !== null || self._exit != null || self._q.length === 0)
return;
self._qq = self._q.shift();
self._proc.stdin.write('::echo ' + START_MARKER + '\n');
if (self._qq.command)
self._proc.stdin.write(self._qq.command + '\n');
self._proc.stdin.write('::echo ' + END_MARKER + '\n');
}
MDB.prototype._handle = function
_handle()
{
var self = this;
var m = MARKER_REGEX.exec(self._data);
if (!m)
return;
/*
* Throw away the data we just consumed.
*/
self._data = self._data.substr(m.index + m[0].length);
/*
* Send the result to the callback:
*/
if (self._qq) {
if (self._qq.callback) {
self._qq.callback(self._qq.command, self._qq.argument,
m[1]);
}
self._qq = null;
}
if (self._q.length === 0 && self._exit) {
self.emit('exit', self._exit.code, self._exit.signal);
return;
}
/*
* Attempt to send the next command to mdb:
*/
self._dispatch();
}
MDB.prototype.command = function
command(cmdstr, argument, callback)
{
var self = this;
if (self._exit) {
throw new (Error('attempt to run command after mdb already ' +
'exited'));
}
self._q.push({
command: cmdstr,
argument: argument,
callback: callback
});
process.nextTick(function() {
self._dispatch();
});
}
MDB.prototype.close = function
close()
{
var self = this;
self._proc.stdin.write('::quit\n');
self._proc.stdin.end();
}
module.exports = {
MDB: MDB
};
/*
* Main Routine:
*/
var PID = process.argv[2];
var KVM_T = null;
if (!PID) {
console.error('Usage: dump_ring_buffer.js <qemu_pid>');
process.exit(1);
}
var TAGS = [
'',
'CTXSAVE',
'CTXRESTORE',
'VMPTRLD',
'VCPUMIGRATE',
'VCPUCLEAR',
'VCPULOAD',
'VCPUPUT',
'RELOAD',
'EMUFAIL0',
'EMUFAIL1',
'EMUFAIL2'
];
var CPULIST = [];
var M = new MDB('-k');
M.command('::walk kvm | ::printf "%x %d\\n" kvm_t . kvm_pid', {}, hdlr0);
function
to_lines(instr)
{
var lines = instr.split(/\n/);
if (!lines[lines.length - 1].trim())
lines.splice(lines.length - 1, 1);
return (lines);
}
function
hdlr0(command, argument, output)
{
var lines = to_lines(output);
for (var i = 0; i < lines.length; i++) {
var m = lines[i].match(/([0-9a-f]+)\s+([0-9]+)/);
if (!m)
continue;
if (m[2] === PID) {
KVM_T = m[1];
break;
}
}
if (!KVM_T) {
console.error('could not find kvm_t for qemu pid %d', PID);
process.exit(1);
}
//console.error('kvm_t @ %s', KVM_T);
M.command(KVM_T + '::print kvm_t vcpus | ::array uint64_t 0t64 |' +
' ::eval ./J', {}, hdlr1);
}
function
hdlr1(command, argument, output)
{
var lines = to_lines(output);
for (var i = 0; i < lines.length; i++) {
var m = lines[i].match(/0x([0-9a-f]+):\s+([0-9a-f]+)/);
if (!m)
continue;
if (m[2] !== '0') {
CPULIST.push({
index: i,
vcpu_t: m[2],
tagcount: {},
ringbuf: []
});
}
}
proc_cpus();
}
var CURCPU = -1;
function
proc_cpus()
{
if (++CURCPU >= CPULIST.length) {
cleanup();
return;
}
var vcpu_t = CPULIST[CURCPU].vcpu_t;
M.command(vcpu_t + '::print kvm_vcpu_t' +
' kvcpu_ringbuf.kvmr_tagcount |' +
' ::array uint32_t 12 | ::eval ./U', {}, hdlr_pc);
function hdlr_pc(command, argument, output) {
var lines = to_lines(output);
for (var i = 0; i < lines.length; i++) {
var m = lines[i].match(
/0x([0-9a-f]+):\s+([0-9]+)/);
if (!m)
continue;
if (i > 0 && i < TAGS.length) {
var num = Number(m[2]);
CPULIST[CURCPU].tagcount[TAGS[i]] = num;
}
}
M.command(vcpu_t + '::print -a kvm_vcpu_t ' +
'kvcpu_ringbuf.kvmr_buf | ::array ' +
'kvm_ringbuf_entry_t 0t512 | ::printf ' +
'"%p %u %u %x %x %x\\n" kvm_ringbuf_entry_t ' +
'. kvmre_tag kvmre_cpuid kvmre_thread kvmre_tsc ' +
'kvmre_payload', {}, hdlr_rb);
}
function hdlr_rb(command, argument, output) {
var lines = to_lines(output);
for (var i = 0; i < lines.length; i++) {
var RE_HEX = '([0-9a-f]+)';
var RE_INT = '([0-9]+)';
var RE_SPC = '\\s+';
var RE = [ RE_HEX, RE_INT, RE_INT, RE_HEX, RE_HEX,
RE_HEX ].join(RE_SPC);
var m = lines[i].match(new RegExp(RE));
if (!m)
continue;
CPULIST[CURCPU].ringbuf.push({
addr: m[1],
tag: TAGS[Number(m[2])] || m[2],
cpuid: Number(m[3]),
thread: m[4],
tsc: m[5],
payload: m[6]
});
}
process.nextTick(proc_cpus);
}
}
function
cleanup()
{
console.log(JSON.stringify(CPULIST));
M.close();
process.exit(0);
}
/* vim: set noet ts=8 sts=8 sw=8: */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment