diff options
Diffstat (limited to 'b2g/components/LogCapture.jsm')
-rw-r--r-- | b2g/components/LogCapture.jsm | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/b2g/components/LogCapture.jsm b/b2g/components/LogCapture.jsm new file mode 100644 index 000000000..803028d57 --- /dev/null +++ b/b2g/components/LogCapture.jsm @@ -0,0 +1,221 @@ +/* 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.importGlobalProperties(['FileReader']); +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 = new FileReader(); + 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; |