gecko-dev/b2g/components/LogCapture.jsm

223 lines
5.9 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* jshint moz: true */
/* global Uint8Array, Components, dump */
"use strict";
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cc = Components.classes;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Screenshot", "resource://gre/modules/Screenshot.jsm");
this.EXPORTED_SYMBOLS = ["LogCapture"];
const SYSTEM_PROPERTY_KEY_MAX = 32;
const SYSTEM_PROPERTY_VALUE_MAX = 92;
function debug(msg) {
dump("LogCapture.jsm: " + msg + "\n");
}
var LogCapture = {
ensureLoaded: function() {
if (!this.ctypes) {
this.load();
}
},
load: function() {
// load in everything on first use
Cu.import("resource://gre/modules/ctypes.jsm", this);
this.libc = this.ctypes.open(this.ctypes.libraryName("c"));
this.read = this.libc.declare("read",
this.ctypes.default_abi,
this.ctypes.int, // bytes read (out)
this.ctypes.int, // file descriptor (in)
this.ctypes.voidptr_t, // buffer to read into (in)
this.ctypes.size_t // size_t size of buffer (in)
);
this.open = this.libc.declare("open",
this.ctypes.default_abi,
this.ctypes.int, // file descriptor (returned)
this.ctypes.char.ptr, // path
this.ctypes.int // flags
);
this.close = this.libc.declare("close",
this.ctypes.default_abi,
this.ctypes.int, // error code (returned)
this.ctypes.int // file descriptor
);
this.getpid = this.libc.declare("getpid",
this.ctypes.default_abi,
this.ctypes.int // PID
);
this.property_find_nth =
this.libc.declare("__system_property_find_nth",
this.ctypes.default_abi,
this.ctypes.voidptr_t, // return value: nullable prop_info*
this.ctypes.unsigned_int); // n: the index of the property to return
this.property_read =
this.libc.declare("__system_property_read",
this.ctypes.default_abi,
this.ctypes.void_t, // return: none
this.ctypes.voidptr_t, // non-null prop_info*
this.ctypes.char.ptr, // key
this.ctypes.char.ptr); // value
this.key_buf = this.ctypes.char.array(SYSTEM_PROPERTY_KEY_MAX)();
this.value_buf = this.ctypes.char.array(SYSTEM_PROPERTY_VALUE_MAX)();
},
cleanup: function() {
this.libc.close();
this.read = null;
this.open = null;
this.close = null;
this.property_find_nth = null;
this.property_read = null;
this.key_buf = null;
this.value_buf = null;
this.libc = null;
this.ctypes = null;
},
/**
* readLogFile
* Read in /dev/log/{{log}} in nonblocking mode, which will return -1 if
* reading would block the thread.
*
* @param log {String} The log from which to read. Must be present in /dev/log
* @return {Uint8Array} Raw log data
*/
readLogFile: function(logLocation) {
this.ensureLoaded();
const O_READONLY = 0;
const O_NONBLOCK = 1 << 11;
const BUF_SIZE = 2048;
let BufType = this.ctypes.ArrayType(this.ctypes.char);
let buf = new BufType(BUF_SIZE);
let logArray = [];
let logFd = this.open(logLocation, O_READONLY | O_NONBLOCK);
if (logFd === -1) {
return null;
}
let readStart = Date.now();
let readCount = 0;
while (true) {
let count = this.read(logFd, buf, BUF_SIZE);
readCount += 1;
if (count <= 0) {
// log has return due to being nonblocking or running out of things
break;
}
for(let i = 0; i < count; i++) {
logArray.push(buf[i]);
}
}
let logTypedArray = new Uint8Array(logArray);
this.close(logFd);
return logTypedArray;
},
/**
* Get all system properties as a dict with keys mapping to values
*/
readProperties: function() {
this.ensureLoaded();
let n = 0;
let propertyDict = {};
while(true) {
let prop_info = this.property_find_nth(n);
if(prop_info.isNull()) {
break;
}
// read the prop_info into the key and value buffers
this.property_read(prop_info, this.key_buf, this.value_buf);
let key = this.key_buf.readString();;
let value = this.value_buf.readString()
propertyDict[key] = value;
n++;
}
return propertyDict;
},
/**
* Dumping about:memory to a file in /data/local/tmp/, returning a Promise.
* Will be resolved with the dumped file name.
*/
readAboutMemory: function() {
this.ensureLoaded();
let deferred = Promise.defer();
// Perform the dump
let dumper = Cc["@mozilla.org/memory-info-dumper;1"]
.getService(Ci.nsIMemoryInfoDumper);
let file = "/data/local/tmp/logshake-about_memory-" + this.getpid() + ".json.gz";
dumper.dumpMemoryReportsToNamedFile(file, function() {
deferred.resolve(file);
}, null, false);
return deferred.promise;
},
/**
* Dumping screenshot, returning a Promise. Will be resolved with the content
* as an ArrayBuffer.
*/
getScreenshot: function() {
let deferred = Promise.defer();
try {
this.ensureLoaded();
let fr = Cc["@mozilla.org/files/filereader;1"]
.createInstance(Ci.nsIDOMFileReader);
fr.onload = function(evt) {
deferred.resolve(new Uint8Array(evt.target.result));
};
fr.onerror = function(evt) {
deferred.reject(evt);
};
fr.readAsArrayBuffer(Screenshot.get());
} catch(e) {
// We pass any errors through to the deferred Promise
deferred.reject(e);
}
return deferred.promise;
}
};
this.LogCapture = LogCapture;