summaryrefslogtreecommitdiffstats
path: root/b2g/components/LogCapture.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'b2g/components/LogCapture.jsm')
-rw-r--r--b2g/components/LogCapture.jsm221
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;