droid-monitor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cacf9200a217f8fddad33f2483e8828389d2f3b0
4
+ data.tar.gz: d72042f303c3d823fa269c43b7a27d29f730e7af
5
+ SHA512:
6
+ metadata.gz: 7397602bc886867993fbc6a857794d3bb276ab5d3b9854bd53dddd5db33b614ff994e2d9f630bf43d11ddaf20ba64dc5c539cffc23455927d75a1224d181fa10
7
+ data.tar.gz: d1dc33a5e971795173bdfcfb79715d2053a07edd86a1b33be4957b307a9ace465588f2cc80e9445390b4178512f226fe345af69744725003330bc3b19cff8d19
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+
15
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in droid-monitor.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Kazuaki MATSUO
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,79 @@
1
+ # Droid::Monitor
2
+
3
+ Monitoring Android apu or memory usage and create their simple graph with Google API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'droid-monitor'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install droid-monitor
20
+
21
+ ## Usage
22
+
23
+ See under `sample` file in this repository.
24
+
25
+ ### CPU
26
+
27
+ ```ruby
28
+ # initialize
29
+ @cpu = Droid::Monitor::Cpu.new( { package: "com.android.chrome" } )
30
+
31
+ # save data into @cpu.cpu_usage
32
+ @cpu.store_dumped_cpu_usage
33
+
34
+ # export data into filename as google api format
35
+ filename = "sample_data.txt"
36
+ @cpu.save_cpu_usage_as_google_api(filename)
37
+
38
+ # export data into filename which is used the above command.
39
+ output_file_path = "sample.html"
40
+ graph_opts = { title: "Example", header1: "this graph is just sample"}
41
+ @cpu.create_graph(filename, graph_opts, output_file_path)
42
+
43
+ ```
44
+
45
+ #### Graph
46
+
47
+ ![](https://github.com/KazuCocoa/droid-monitor/blob/master/doc/images/Screen%20Shot%202015-05-23%20at%2019.46.08.png)
48
+
49
+ ### Memory
50
+
51
+ ```ruby
52
+ # initialize
53
+ @memory = Droid::Monitor::Cpu.new( { package: "com.android.chrome" } )
54
+
55
+ # save data into @memory.memory_usage
56
+ @memory.store_dumped_memory_details_usage
57
+
58
+ # export data into filename as google api format
59
+ filename = "sample_data.txt"
60
+ @memory.save_memory_details_as_google_api(filename)
61
+
62
+ # export data into filename which is used the above command.
63
+ output_file_path = "sample.html"
64
+ graph_opts = { title: "Example", header1: "this graph is just sample"}
65
+ @cpu.create_graph(filename, graph_opts, output_file_path)
66
+ ```
67
+
68
+ #### Graph
69
+
70
+ ![](https://github.com/KazuCocoa/droid-monitor/blob/master/doc/images/Screen%20Shot%202015-05-23%20at%2019.56.41.png)
71
+
72
+
73
+ ## Contributing
74
+
75
+ 1. Fork it ( https://github.com/[my-github-username]/droid-monitor/fork )
76
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
77
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
78
+ 4. Push to the branch (`git push origin my-new-feature`)
79
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'droid/monitor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "droid-monitor"
8
+ spec.version = Droid::Monitor::VERSION
9
+ spec.authors = ["Kazuaki MATSUO"]
10
+ spec.email = ["fly.49.89.over@gmail.com"]
11
+ spec.summary = %q{Monitoring Android cpu or memory usage and create their simple graph with Google API.}
12
+ spec.description = %q{Monitoring connected Android devices via adb command. And you can create simple http graph wth Google API.}
13
+ spec.homepage = "https://github.com/KazuCocoa/droid-monitor"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+ spec.required_ruby_version = ">= 2.0.0"
21
+
22
+ spec.add_dependency "faml"
23
+ spec.add_dependency "tilt"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "test-unit"
26
+ end
@@ -0,0 +1,49 @@
1
+ require_relative "monitor/version"
2
+
3
+ module Droid
4
+ module Monitor
5
+ class Adb
6
+ attr_accessor :package, :device_serial, :api_level
7
+
8
+ def initialize( opts = {})
9
+ fail 'opts must be a hash' unless opts.is_a? Hash
10
+ fail 'Package name is required.' unless opts[:package]
11
+ @package = opts[:package]
12
+ @device_serial = opts[:device_serial] || ""
13
+ @api_level = device_sdk_version
14
+ end
15
+
16
+ # @return [Integer] message from adb command
17
+ # e.g: 17
18
+ def device_sdk_version
19
+ `#{adb_shell} getprop ro.build.version.sdk`.chomp.to_i
20
+ end
21
+
22
+ # @return [String] message from adb command
23
+ def dump_cpuinfo
24
+ `#{adb_shell} dumpsys cpuinfo`.chomp
25
+ end
26
+
27
+ # @return [String] message from adb command
28
+ def dump_meminfo
29
+ `#{adb_shell} dumpsys meminfo #{@package}`.chomp
30
+ end
31
+
32
+ private
33
+
34
+ def adb
35
+ fail "ANDROID_HOME is not set" unless ENV["ANDROID_HOME"]
36
+ "#{ENV["ANDROID_HOME"]}/platform-tools/adb"
37
+ end
38
+
39
+ def device_serial_option
40
+ return "" unless @device_serial && @device_serial != ""
41
+ "-s \"#{@device_serial}\""
42
+ end
43
+
44
+ def adb_shell
45
+ "#{adb} #{device_serial_option} shell"
46
+ end
47
+ end # class Adb
48
+ end # module Monitor
49
+ end # module Droid
@@ -0,0 +1 @@
1
+ require_relative "utils"
@@ -0,0 +1,15 @@
1
+ module Droid
2
+ module Monitor
3
+ module Utils
4
+ def merge_current_time(hash)
5
+ hash.merge(time: Time.now.strftime('%T.%L'))
6
+ end
7
+
8
+ def save(data, into_file_path)
9
+ File.open(into_file_path, 'w') do |file|
10
+ file.puts data
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,110 @@
1
+ require_relative "../monitor"
2
+ require_relative "common/commons"
3
+ require_relative "report/google_api_template"
4
+
5
+ require "json"
6
+
7
+ module Droid
8
+ module Monitor
9
+ class Cpu < Droid::Monitor::Adb
10
+ attr_reader :cpu_usage
11
+
12
+ include Droid::Monitor::Utils
13
+
14
+ def initialize(opts = {})
15
+ super(opts)
16
+ @cpu_usage = []
17
+ end
18
+
19
+ def clear_cpu_usage
20
+ @cpu_usage = []
21
+ end
22
+
23
+ def dump_cpu_usage(dump_data)
24
+ dump = dump_data.scan(/^.*#{self.package}.*$/).map(&:strip).first.split(/\s/).reject(&:empty?)
25
+ fail 'no package' if /^Load:$/ =~ dump[0]
26
+ dump
27
+ rescue StandardError => e
28
+ puts e
29
+ []
30
+ end
31
+
32
+ # called directory
33
+ def store_dumped_cpu_usage
34
+ self.store_cpu_usage(self.dump_cpu_usage(self.dump_cpuinfo))
35
+ end
36
+
37
+ def save_cpu_usage_as_google_api(file_path)
38
+ self.save(export_as_google_api_format(@cpu_usage), file_path)
39
+ end
40
+
41
+ def store_cpu_usage(dumped_cpu)
42
+ @cpu_usage.push self.merge_current_time(transfer_total_cpu_to_hash(dumped_cpu))
43
+ end
44
+
45
+ def transfer_total_cpu_to_hash(dump_cpu_array)
46
+ if dump_cpu_array.length == 0
47
+ {
48
+ total_cpu: '0%',
49
+ process: 'no package process',
50
+ user: '0%',
51
+ kernel: '0%',
52
+ time: dump_cpu_array.last,
53
+ }
54
+ else
55
+ {
56
+ total_cpu: dump_cpu_array[0],
57
+ process: dump_cpu_array[1],
58
+ user: dump_cpu_array[2],
59
+ kernel: dump_cpu_array[5],
60
+ time: dump_cpu_array.last,
61
+ }
62
+ end
63
+ end
64
+
65
+ def export_as_google_api_format(from_cpu_usage)
66
+ google_api_data_format = empty_google_api_format
67
+
68
+ from_cpu_usage.each do |hash|
69
+
70
+ puts hash
71
+ a_google_api_data_format = {
72
+ c: [
73
+ { v: hash[:time] },
74
+ { v: hash[:total_cpu].delete('%').to_f },
75
+ { v: hash[:user].delete('%').to_f },
76
+ { v: hash[:kernel].delete('%').to_f },
77
+ ]
78
+ }
79
+ google_api_data_format[:rows].push(a_google_api_data_format)
80
+ end
81
+
82
+ JSON.generate google_api_data_format
83
+ end
84
+
85
+ # @params [String] data_file_path A path to data.
86
+ # @params [Hash] graph_opts A hash regarding graph settings.
87
+ # @params [String] output_file_path A path you would like to export data.
88
+ def create_graph(data_file_path, graph_opts = {}, output_file_path)
89
+ self.save(Droid::Monitor::GoogleApiTemplate.create_graph(data_file_path, graph_opts),
90
+ output_file_path)
91
+ end
92
+
93
+ private
94
+
95
+ def empty_google_api_format
96
+ {
97
+ cols: [
98
+ { label: 'time', type: 'string' },
99
+ { label: 'total_cpu', type: 'number' },
100
+ { label: 'user', type: 'number' },
101
+ { label: 'kernel', type: 'number' },
102
+ ],
103
+ rows: [
104
+ ],
105
+ }
106
+ end
107
+
108
+ end # class Cpu
109
+ end # module Monitor
110
+ end # module Droid
@@ -0,0 +1,202 @@
1
+ require_relative "../monitor"
2
+ require_relative "common/commons"
3
+ require_relative "report/google_api_template"
4
+
5
+ require "json"
6
+
7
+ module Droid
8
+ module Monitor
9
+ class Memory < Droid::Monitor::Adb
10
+ attr_reader :memory_usage, :memory_detail_usage
11
+
12
+ include Droid::Monitor::Utils
13
+
14
+ def initialize(opts = {})
15
+ super(opts)
16
+ @memory_usage = []
17
+ @memory_detail_usage = []
18
+ end
19
+
20
+ def clear_memory_usage
21
+ @memory_usage = []
22
+ end
23
+
24
+ def clear_memory_detail_usage
25
+ @memory_detail_usage = []
26
+ end
27
+
28
+ def dump_memory_usage(dump_data)
29
+ fail "no process" if dump_data == "No process found for: #{@package}"
30
+ dump_data.scan(/^.*Uptime.*Realtime.*$/).map(&:strip).first.split(/\s/).reject(&:empty?)
31
+ rescue StandardError => e
32
+ puts e
33
+ []
34
+ end
35
+
36
+ def dump_memory_details_usage(dump_data)
37
+ fail "no process" if dump_data == "No process found for: #{@package}"
38
+ dump_data.scan(/^.*TOTAL.*$/).map(&:strip).first.split(/\s/).reject(&:empty?)
39
+ rescue StandardError => e
40
+ puts e
41
+ []
42
+ end
43
+
44
+ # called directory
45
+ def store_dumped_memory_usage
46
+ self.store_memory_usage(self.dump_memory_usage(self.dump_meminfo))
47
+ end
48
+
49
+ # called directory
50
+ def store_dumped_memory_details_usage
51
+ self.store_memory_details_usage(self.dump_memory_details_usage(self.dump_meminfo))
52
+ end
53
+
54
+ def save_memory_as_google_api(file_path)
55
+ self.save(export_as_google_api_format(@memory_usage), file_path)
56
+ end
57
+
58
+ def save_memory_details_as_google_api(file_path)
59
+ self.save(export_as_google_api_format(@memory_detail_usage), file_path)
60
+ end
61
+
62
+ # @param [Array] dumped_memory Array of dumped memory data
63
+ # @return [Hash] Google API formatted data
64
+ def store_memory_usage(dumped_memory)
65
+ @memory_usage.push self.merge_current_time(transfer_total_memory_to_hash(dumped_memory))
66
+ end
67
+
68
+ # @param [Array] dumped_memory_details Array of dumped memory data
69
+ # @return [Hash] Google API formatted data
70
+ def store_memory_details_usage(dumped_memory_details)
71
+ @memory_detail_usage.push self.merge_current_time(transfer_total_memory_details_to_hash(dumped_memory_details))
72
+ end
73
+
74
+ def transfer_total_memory_to_hash(data)
75
+ total_memory(data)
76
+ end
77
+
78
+ def transfer_total_memory_details_to_hash(data)
79
+ if @api_level.to_i >= 19
80
+ total_memory_details_api_level_over_44(data)
81
+ else
82
+ total_memory_details_api_level_under_43(data)
83
+ end
84
+ end
85
+
86
+ def export_as_google_api_format(data)
87
+ if @api_level.to_i >= 19
88
+ google_api_data_format = empty_google_api_format_over44
89
+ data.each do |hash|
90
+ a_google_api_data_format = {
91
+ c: [
92
+ { v: hash[:time] },
93
+ { v: hash[:pss_total] },
94
+ { v: hash[:private_dirty] },
95
+ { v: hash[:private_clean] },
96
+ { v: hash[:swapped_dirty] },
97
+ { v: hash[:heap_size] },
98
+ { v: hash[:heap_alloc] },
99
+ { v: hash[:heap_free] },
100
+ ],
101
+ }
102
+ google_api_data_format[:rows].push(a_google_api_data_format)
103
+ end
104
+ JSON.generate google_api_data_format
105
+ else
106
+ google_api_data_format = empty_google_api_format_over43
107
+
108
+ data.each do |hash|
109
+ a_google_api_data_format = {
110
+ c: [
111
+ { v: hash[:time] },
112
+ { v: hash[:pss_total] },
113
+ { v: hash[:shared_dirty] },
114
+ { v: hash[:private_dirty] },
115
+ { v: hash[:heap_size] },
116
+ { v: hash[:heap_alloc] },
117
+ { v: hash[:heap_free] },
118
+ ],
119
+ }
120
+ google_api_data_format[:rows].push(a_google_api_data_format)
121
+ end
122
+
123
+ JSON.generate google_api_data_format
124
+ end
125
+ end
126
+
127
+ # @params [String] data_file_path A path to data.
128
+ # @params [Hash] graph_opts A hash regarding graph settings.
129
+ # @params [String] output_file_path A path you would like to export data.
130
+ def create_graph(data_file_path, graph_opts = {}, output_file_path)
131
+ self.save(Droid::Monitor::GoogleApiTemplate.create_graph(data_file_path, graph_opts),
132
+ output_file_path)
133
+ end
134
+
135
+ private
136
+
137
+ def empty_google_api_format_over44
138
+ {
139
+ cols: [
140
+ { label: 'time', type: 'string' },
141
+ { label: 'pss_total', type: 'number' },
142
+ { label: 'private_dirty', type: 'number' },
143
+ { label: 'private_clean', type: 'number' },
144
+ { label: 'swapped_dirty', type: 'number' },
145
+ { label: 'heap_size', type: 'number' },
146
+ { label: 'heap_alloc', type: 'number' },
147
+ { label: 'heap_free', type: 'number' },
148
+ ],
149
+ rows: [
150
+ ],
151
+ }
152
+ end
153
+
154
+ def empty_google_api_format_over43
155
+ {
156
+ cols: [
157
+ { label: 'time', type: 'string' },
158
+ { label: 'pss_total', type: 'number' },
159
+ { label: 'shared_dirty', type: 'number' },
160
+ { label: 'private_dirty', type: 'number' },
161
+ { label: 'heap_size', type: 'number' },
162
+ { label: 'heap_alloc', type: 'number' },
163
+ { label: 'heap_free', type: 'number' },
164
+ ],
165
+ rows: [
166
+ ],
167
+ }
168
+ end
169
+
170
+ def total_memory(data)
171
+ {
172
+ uptime: data[1].to_i ||= 0,
173
+ realtime: data[3].to_i ||= 0,
174
+ }
175
+ end
176
+
177
+ def total_memory_details_api_level_over_44(data)
178
+ {
179
+ pss_total: data[1].to_i ||= 0,
180
+ private_dirty: data[2].to_i ||= 0,
181
+ private_clean: data[3].to_i ||= 0,
182
+ swapped_dirty: data[4].to_i ||= 0,
183
+ heap_size: data[5].to_i ||= 0,
184
+ heap_alloc: data[6].to_i ||= 0,
185
+ heap_free: data[7].to_i ||= 0,
186
+ }
187
+ end
188
+
189
+ def total_memory_details_api_level_under_43(data)
190
+ {
191
+ pss_total: data[1].to_i ||= 0,
192
+ shared_dirty: data[2].to_i ||= 0,
193
+ private_dirty: data[3].to_i ||= 0,
194
+ heap_size: data[4].to_i ||= 0,
195
+ heap_alloc: data[5].to_i ||= 0,
196
+ heap_free: data[6].to_i ||= 0,
197
+ }
198
+ end
199
+
200
+ end # class Memory
201
+ end # module Monitor
202
+ end # module Droid