├── .jshintignore
├── test
├── mocha.opts
└── android-performance.test.js
├── .gitignore
├── index.js
├── lib
├── helper.js
└── android-performance.js
├── .jshintrc
├── .travis.yml
├── package.json
└── README.md
/.jshintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | coverage/
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --require should
2 | --reporter spec
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | coverage
4 | *.sw*
5 | *.un~
6 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = require('./lib/android-performance');
4 |
--------------------------------------------------------------------------------
/lib/helper.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const macacaUtils = require('xutil');
4 |
5 | const _ = macacaUtils.merge({}, macacaUtils);
6 |
7 | module.exports = _;
8 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "indent": 2,
3 | "latedef": "nofunc",
4 | "newcap": true,
5 | "curly": true,
6 | "eqeqeq": true,
7 | "eqnull": true,
8 | "strict": true,
9 | "undef": true,
10 | "unused": true,
11 | "laxcomma": false,
12 | "asi": false,
13 | "boss": false,
14 | "expr": true,
15 | "sub": true,
16 | "quotmark": "single",
17 | "loopfunc": false,
18 | "lastsemic": true,
19 | "funcscope": false,
20 | "esnext": true,
21 | "noyield": true,
22 | "browser": true,
23 | "nonstandard": true,
24 | "devel": true,
25 | "node": true,
26 | "mocha": true,
27 | "validthis": true,
28 | "globals": {
29 | "define": false
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | sudo: false
3 | addons:
4 | apt:
5 | packages:
6 | - xvfb
7 | jdk:
8 | - openjdk7
9 | android:
10 | components:
11 | - build-tools-23.0.2
12 | - android-23
13 | - extra-android-m2repository
14 | - extra-android-support
15 | before_install:
16 | - export CHROME_BIN=chromium-browser
17 | - export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
18 | - export ANDROID_HOME=/usr/local/android-sdk
19 | - echo yes | android update sdk --all --filter build-tools-23.0.2 --no-ui --force
20 | - export DISPLAY=:99.0
21 | - sh -e /etc/init.d/xvfb start
22 | before_script:
23 | - . $HOME/.nvm/nvm.sh
24 | - nvm install 4
25 | - nvm use 4
26 | - npm install
27 | - echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a
28 | - emulator -avd test -no-audio -no-window &
29 | - android-wait-for-emulator
30 | - adb shell input keyevent 82 &
31 | script: "make test"
32 | after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls"
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "android-performance",
3 | "version": "1.0.2",
4 | "description": "Node.js wrapper to android performance with adb",
5 | "keywords": [
6 | "android",
7 | "performance"
8 | ],
9 | "files": [
10 | "index.js",
11 | "lib/*.js"
12 | ],
13 | "main": "index.js",
14 | "repository": {
15 | "type": "git",
16 | "url": "git://github.com/xudafeng/android-performance.git"
17 | },
18 | "dependencies": {
19 | "macaca-adb": "^1.0.16",
20 | "xutil": "^1.0.1"
21 | },
22 | "devDependencies": {
23 | "co-mocha": "*",
24 | "git-contributor": "1",
25 | "istanbul": "*",
26 | "jshint": "*",
27 | "mocha": "*",
28 | "pre-commit": "1.1.1",
29 | "should": "*"
30 | },
31 | "scripts": {
32 | "test": "make test",
33 | "jshint": "make jshint",
34 | "contributor": "git-contributor"
35 | },
36 | "pre-commit": [
37 | "jshint"
38 | ],
39 | "homepage": "https://github.com/xudafeng/android-performance",
40 | "license": "MIT"
41 | }
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # android-performance
2 |
3 | [![NPM version][npm-image]][npm-url]
4 | [![build status][travis-image]][travis-url]
5 | [![Test coverage][coveralls-image]][coveralls-url]
6 | [![node version][node-image]][node-url]
7 | [![npm download][download-image]][download-url]
8 |
9 | [npm-image]: https://img.shields.io/npm/v/android-performance.svg?style=flat-square
10 | [npm-url]: https://npmjs.org/package/android-performance
11 | [travis-image]: https://img.shields.io/travis/xudafeng/android-performance.svg?style=flat-square
12 | [travis-url]: https://travis-ci.org/xudafeng/android-performance
13 | [coveralls-image]: https://img.shields.io/coveralls/xudafeng/android-performance.svg?style=flat-square
14 | [coveralls-url]: https://coveralls.io/r/xudafeng/android-performance?branch=master
15 | [node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square
16 | [node-url]: http://nodejs.org/download/
17 | [download-image]: https://img.shields.io/npm/dm/android-performance.svg?style=flat-square
18 | [download-url]: https://npmjs.org/package/android-performance
19 |
20 | > Node.js wrapper to android performance with adb
21 |
22 | ## Installment
23 |
24 | ```bash
25 | $ npm i android-performance --save-dev
26 | ```
27 |
28 | ## Usage
29 |
30 | ```javascript
31 | const perf = new AndroidPerformance();
32 | const p1 = new Promise((resolve, reject) => {
33 | perf
34 | .initDevice()
35 | .then(() => perf.getMeminfoByPackageName(pkgName))
36 | .then(res => {
37 | resolve({
38 | item: 'Meminfo',
39 | data: res
40 | });
41 | });
42 | });
43 |
44 | const p2 = new Promise((resolve, reject) => {
45 | perf
46 | .initDevice()
47 | .then(() => perf.getPid(pkgName))
48 | .then(pid => {
49 | return perf
50 | .getThreadCountByPid(pid)
51 | .then(d => {
52 | resolve({
53 | item: 'ThreadCount',
54 | data: d
55 | });
56 | })
57 | .catch(e => {
58 | resolve(null);
59 | });
60 | })
61 | .catch(e => {
62 | resolve(null);
63 | });
64 | });
65 |
66 | const p3 = new Promise((resolve, reject) => {
67 | perf
68 | .initDevice()
69 | .then(() => perf.getPid(pkgName))
70 | .then(pid => {
71 | return perf
72 | .getUidByPid(pid)
73 | .then(uid => {
74 | return perf
75 | .getTrafficByUid(uid)
76 | .then(d => {
77 | resolve({
78 | item: 'Traffic',
79 | data: d
80 | });
81 | })
82 | .catch(e => {
83 | resolve(null);
84 | });
85 | })
86 | .catch(e => {
87 | resolve(null);
88 | });
89 | })
90 | .catch(e => {
91 | resolve(null);
92 | });
93 | });
94 |
95 | const p4 = new Promise((resolve, reject) => {
96 | perf
97 | .initDevice()
98 | .then(() => perf.getPid(pkgName))
99 | .then(pid => {
100 | return perf
101 | .getCPUByPid(pid)
102 | .then(d => {
103 | resolve({
104 | item: 'cpu',
105 | data: d
106 | });
107 | })
108 | .catch(e => {
109 | resolve(null);
110 | });
111 | });
112 | });
113 |
114 | Promise.all([p1, p2, p3, p4]).then(result => {
115 | console.log(`performance:${JSON.stringify(result)}`);
116 | done();
117 | });
118 | ```
119 |
120 |
121 |
122 | ## Contributors
123 |
124 | |[
xudafeng](https://github.com/xudafeng)
|[
supern](https://github.com/supern)
|[
zhuyali](https://github.com/zhuyali)
|
125 | | :---: | :---: | :---: |
126 |
127 |
128 | This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Sun Jul 18 2021 11:15:58 GMT+0800`.
129 |
130 |
131 |
132 | ## License
133 |
134 | The MIT License (MIT)
--------------------------------------------------------------------------------
/test/android-performance.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const AndroidPerformance = require('..');
4 |
5 | const pkgName = 'com.android.phone';
6 |
7 | describe('test', function() {
8 | it('should be ok', function() {
9 | console.log(AndroidPerformance);
10 | AndroidPerformance.should.be.ok();
11 | });
12 |
13 | it('should init device success', function(done) {
14 | const perf = new AndroidPerformance();
15 | perf.initDevice(function(err, device) {
16 | if (err) {
17 | console.log(err);
18 | done();
19 | }
20 | console.log(device);
21 | done();
22 | });
23 | });
24 |
25 | it('should get meminfo success', function *() {
26 | const perf = new AndroidPerformance();
27 | yield perf.initDevice();
28 | const res = yield perf.getMeminfoByPackageName(pkgName);
29 | console.log(res);
30 | });
31 |
32 | it('should get pid success', function *() {
33 | const perf = new AndroidPerformance();
34 | yield perf.initDevice();
35 | const res = yield perf.getPid(pkgName);
36 | console.log(res);
37 | });
38 |
39 | it('should get threadcount success', function *() {
40 | const perf = new AndroidPerformance();
41 | yield perf.initDevice();
42 | const pid = yield perf.getPid(pkgName);
43 | const res = yield perf.getThreadCountByPid(pid);
44 | console.log(res);
45 | });
46 |
47 | it('should get uid success', function *() {
48 | const perf = new AndroidPerformance();
49 | yield perf.initDevice();
50 | const pid = yield perf.getPid(pkgName);
51 | const uid = yield perf.getUidByPid(pid);
52 | console.log(uid);
53 | });
54 |
55 | it('should get traffic success', function *() {
56 | const perf = new AndroidPerformance();
57 | yield perf.initDevice();
58 | const pid = yield perf.getPid(pkgName);
59 | const uid = yield perf.getUidByPid(pid);
60 | const res = yield perf.getTrafficByUid(uid);
61 | console.log(res);
62 | });
63 |
64 | it('should get CPU success', function *() {
65 | const perf = new AndroidPerformance();
66 | yield perf.initDevice();
67 | const pid = yield perf.getPid(pkgName);
68 | const res = yield perf.getCPUByPid(pid);
69 | console.log(res);
70 | });
71 |
72 | it('should all in one with promise', function(done) {
73 | const perf = new AndroidPerformance();
74 | const p1 = new Promise((resolve, reject) => {
75 | perf
76 | .initDevice()
77 | .then(() => perf.getMeminfoByPackageName(pkgName))
78 | .then(res => {
79 | resolve({
80 | item: 'Meminfo',
81 | data: res
82 | });
83 | });
84 | });
85 |
86 | const p2 = new Promise((resolve, reject) => {
87 | perf
88 | .initDevice()
89 | .then(() => perf.getPid(pkgName))
90 | .then(pid => {
91 | return perf
92 | .getThreadCountByPid(pid)
93 | .then(d => {
94 | resolve({
95 | item: 'ThreadCount',
96 | data: d
97 | });
98 | })
99 | .catch(e => {
100 | resolve(null);
101 | });
102 | })
103 | .catch(e => {
104 | resolve(null);
105 | });
106 | });
107 |
108 | const p3 = new Promise((resolve, reject) => {
109 | perf
110 | .initDevice()
111 | .then(() => perf.getPid(pkgName))
112 | .then(pid => {
113 | return perf
114 | .getUidByPid(pid)
115 | .then(uid => {
116 | return perf
117 | .getTrafficByUid(uid)
118 | .then(d => {
119 | resolve({
120 | item: 'Traffic',
121 | data: d
122 | });
123 | })
124 | .catch(e => {
125 | resolve(null);
126 | });
127 | })
128 | .catch(e => {
129 | resolve(null);
130 | });
131 | })
132 | .catch(e => {
133 | resolve(null);
134 | });
135 | });
136 |
137 | const p4 = new Promise((resolve, reject) => {
138 | perf
139 | .initDevice()
140 | .then(() => perf.getPid(pkgName))
141 | .then(pid => {
142 | return perf
143 | .getCPUByPid(pid)
144 | .then(d => {
145 | resolve({
146 | item: 'cpu',
147 | data: d
148 | });
149 | })
150 | .catch(e => {
151 | resolve(null);
152 | });
153 | });
154 | });
155 |
156 | Promise.all([p1, p2, p3, p4]).then(result => {
157 | console.log(`performance:${JSON.stringify(result)}`);
158 | done();
159 | });
160 | });
161 | });
162 |
--------------------------------------------------------------------------------
/lib/android-performance.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const ADB = require('macaca-adb');
4 |
5 | const _ = require('./helper');
6 |
7 | function AndroidPerformance() {
8 | this.adb = null;
9 | this.udid = null;
10 | this.init();
11 | }
12 |
13 | AndroidPerformance.prototype.init = function() {
14 | this.initAdb();
15 | };
16 |
17 | AndroidPerformance.prototype.initAdb = function() {
18 | this.adb = new ADB();
19 | };
20 |
21 | AndroidPerformance.prototype.initDevice = function() {
22 | var args = Array.prototype.slice.call(arguments);
23 |
24 | var promise = new Promise((resolve, reject) => {
25 | ADB.getDevices().then(devices => {
26 | if (devices.length) {
27 | var device = devices[0];
28 | this.adb.setDeviceId(device.udid);
29 | this.udid = device.udid;
30 | resolve(devices);
31 | } else {
32 | console.log('no device');
33 | }
34 | }).catch(err => {
35 | reject(`get devices error with: ${err}`);
36 | });
37 | });
38 |
39 | if (args.length) {
40 | var cb = args[0];
41 | promise.then(data => {
42 | cb.call(this, null, data);
43 | }).catch(err => {
44 | cb.call(this, err);
45 | });
46 | } else {
47 | return promise;
48 | }
49 | };
50 |
51 | AndroidPerformance.prototype.getMeminfoByPackageName = function() {
52 | var args = Array.prototype.slice.call(arguments);
53 | var name = args[0];
54 |
55 | var cmd = `dumpsys meminfo ${name}`;
56 | var parser = function(data) {
57 | return _.find(data.split('\n'), function(line) {
58 | return _.startsWith(line.trim(), 'TOTAL ');
59 | }).trim().split(/\s+/g);
60 | };
61 |
62 | var promise = new Promise((resolve, reject) => {
63 | this.adb.shell(cmd).then(data => {
64 | var sec = parser(data);
65 | resolve(sec[1]);
66 | }).catch(err => {
67 | reject(`exec ${cmd} error with: ${err}`);
68 | });
69 | });
70 |
71 | if (args.length > 1) {
72 | var cb = args[1];
73 | promise.then(data => {
74 | var sec = parser(data);
75 | cb.call(this, null, sec[1]);
76 | }).catch(err => {
77 | cb.call(this, err);
78 | });
79 | } else {
80 | return promise;
81 | }
82 | };
83 |
84 | AndroidPerformance.prototype.getPid = function() {
85 | var args = Array.prototype.slice.call(arguments);
86 | var name = args[0];
87 |
88 | var cmd = `ps`;
89 | var parser = function(data) {
90 | return _.find(data.split('\n'), function(line) {
91 | return line.split(/\s+/g)[8] === name;
92 | }).trim().split(/\s+/g);
93 | };
94 |
95 | var promise = new Promise((resolve, reject) => {
96 | this.adb.shell(cmd).then(data => {
97 | var sec = parser(data);
98 | resolve(sec[1]);
99 | }).catch(err => {
100 | reject(`exec ${cmd} error with: ${err}`);
101 | });
102 | });
103 |
104 | if (args.length > 1) {
105 | var cb = args[1];
106 | promise.then(data => {
107 | var sec = parser(data);
108 | cb.call(this, null, sec[1]);
109 | }).catch(err => {
110 | cb.call(this, err);
111 | });
112 | } else {
113 | return promise;
114 | }
115 | };
116 |
117 | AndroidPerformance.prototype.getThreadCountByPid = function() {
118 | var args = Array.prototype.slice.call(arguments);
119 | var pid = args[0];
120 |
121 | var cmd = `cat /proc/${pid}/status`;
122 | var parser = function(data) {
123 | return _.find(data.split('\n'), function(line) {
124 | return line.includes('Threads');
125 | }).trim().split(/\s+/g);
126 | };
127 |
128 | var promise = new Promise((resolve, reject) => {
129 | this.adb.shell(cmd).then(data => {
130 | var sec = parser(data);
131 | resolve(sec[1]);
132 | }).catch(err => {
133 | reject(`exec ${cmd} error with: ${err}`);
134 | });
135 | });
136 |
137 | if (args.length > 1) {
138 | var cb = args[1];
139 | promise.then(data => {
140 | var sec = parser(data);
141 | cb.call(this, null, sec[1]);
142 | }).catch(err => {
143 | cb.call(this, err);
144 | });
145 | } else {
146 | return promise;
147 | }
148 | };
149 |
150 | AndroidPerformance.prototype.getUidByPid = function() {
151 | var args = Array.prototype.slice.call(arguments);
152 | var pid = args[0];
153 |
154 | var cmd = `cat /proc/${pid}/status`;
155 | var parser = function(data) {
156 | return _.find(data.split('\n'), function(line) {
157 | return line.includes('Uid');
158 | }).trim().split(/\s+/g);
159 | };
160 |
161 | var promise = new Promise((resolve, reject) => {
162 | this.adb.shell(cmd).then(data => {
163 | var sec = parser(data);
164 | resolve(sec[1]);
165 | }).catch(err => {
166 | reject(`exec ${cmd} error with: ${err}`);
167 | });
168 | });
169 |
170 | if (args.length > 1) {
171 | var cb = args[1];
172 | promise.then(data => {
173 | var sec = parser(data);
174 | cb.call(this, null, sec[1]);
175 | }).catch(err => {
176 | cb.call(this, err);
177 | });
178 | } else {
179 | return promise;
180 | }
181 | };
182 |
183 | AndroidPerformance.prototype.getTrafficByUid = function() {
184 | var args = Array.prototype.slice.call(arguments);
185 | var uid = args[0];
186 |
187 | var cmd = `cat /proc/net/xt_qtaguid/stats`;
188 | var parser = function(data) {
189 | var res = {
190 | wifi: {
191 | rcv: 0,
192 | snd: 0
193 | },
194 | mobile: {
195 | rcv: 0,
196 | snd: 0
197 | }
198 | };
199 | var uidx, typex, rcvx, sndx;
200 |
201 | _.forEach(data.split('\n'), function(line) {
202 | var token = line.trim().split(/\s+/g);
203 |
204 | if (token[0] === 'idx') {
205 | uidx = _.indexOf(token, 'uid_tag_int');
206 | typex = _.indexOf(token, 'iface');
207 | rcvx = _.indexOf(token, 'rx_bytes');
208 | sndx = _.indexOf(token, 'tx_bytes');
209 | } else if (token[uidx] === uid) {
210 | if (token[typex].includes('wlan')) {
211 | res.wifi.rcv += parseInt(token[rcvx], 10);
212 | res.wifi.snd += parseInt(token[sndx], 10);
213 | } else {
214 | res.mobile.rcv += parseInt(token[rcvx], 10);
215 | res.mobile.rcv += parseInt(token[sndx], 10);
216 | }
217 | }
218 | });
219 | return res;
220 | };
221 |
222 | var promise = new Promise((resolve, reject) => {
223 | this.adb.shell(cmd).then(data => {
224 | var res = parser(data);
225 | resolve(res);
226 | }).catch(err => {
227 | reject(`exec ${cmd} error with: ${err}`);
228 | });
229 | });
230 |
231 | if (args.length > 1) {
232 | var cb = args[1];
233 | promise.then(data => {
234 | var res = parser(data);
235 | cb.call(this, null, res);
236 | }).catch(err => {
237 | cb.call(this, err);
238 | });
239 | } else {
240 | return promise;
241 | }
242 | };
243 |
244 | AndroidPerformance.prototype.getCPUByPid = function() {
245 | var args = Array.prototype.slice.call(arguments);
246 | var pid = args[0];
247 |
248 | var cmd = `top -n 1`;
249 | var parser = function(data) {
250 | return _.find(data.split('\n'), function(line) {
251 | return line.includes(pid);
252 | }).trim().split(/\s+/g);
253 | };
254 |
255 | var promise = new Promise((resolve, reject) => {
256 | this.adb.shell(cmd).then(data => {
257 | var sec = parser(data);
258 | resolve(sec[2]);
259 | }).catch(err => {
260 | reject(`exec ${cmd} error with: ${err}`);
261 | });
262 | });
263 |
264 | if (args.length > 1) {
265 | var cb = args[1];
266 | promise.then(data => {
267 | var sec = parser(data);
268 | cb.call(this, null, sec[2]);
269 | }).catch(err => {
270 | cb.call(this, err);
271 | });
272 | } else {
273 | return promise;
274 | }
275 | };
276 |
277 | module.exports = AndroidPerformance;
278 |
--------------------------------------------------------------------------------