summaryrefslogtreecommitdiffstats
path: root/b2g/components/LogCapture.jsm
blob: 803028d5749b23bc17a71d6949763ca974faa45d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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;