├── .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 | --------------------------------------------------------------------------------