instrumentality 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 05ae3b363734c9eb4a9c6cbbbb3b2d2f7e4c3d87
4
+ data.tar.gz: 8ac7c4c91afa271574d1e78e8a09b69eaf48badc
5
+ SHA512:
6
+ metadata.gz: 4fb9e519ea950637947cc7e06ed3fd895966bd8d554f1016fb1cb7b1b1ea7cab5a3ebed8ad8c9abdbe8ec10b0329b1f4de6f5a1e4d811c5343b0c8eb6f618eb7
7
+ data.tar.gz: 5432bc1923d7091db9920d82f8e8bfd6bcc50c55026ee7fe51b33e8a332ebde863c899eac44048e9645eed68d80e8b7aa1ea8c6f07f147f2367817e5c01a91c6
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.7
5
+ before_install: gem install bundler -v 1.15.4
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in instrumentality.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Renzo Crisóstomo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Instrumentality
2
+
3
+ Command line interface to profiling tools for iOS development
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'instrumentality'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install instrumentality
20
+
21
+ ## Usage
22
+
23
+ $ instr --help
24
+
25
+ ## Development
26
+
27
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Ruenzuo/instrumentality.
32
+
33
+ ## License
34
+
35
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/instr ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', File.dirname(__FILE__))
4
+ require 'instrumentality'
5
+
6
+ Instrumentality.run
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', File.dirname(__FILE__))
4
+ require 'instrumentality'
5
+
6
+ Instrumentality.run
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "instrumentality/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "instrumentality"
8
+ spec.version = Instrumentality::VERSION
9
+ spec.authors = ["Renzo Crisóstomo"]
10
+ spec.email = ["renzo.crisostomo@here.com"]
11
+
12
+ spec.summary = %q{Command line interface to profiling tools for iOS development}
13
+ spec.description = %q{Command line interface to profiling tools for iOS development}
14
+ spec.homepage = "https://github.com/Ruenzuo/instrumentality"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "bin"
21
+ spec.executables = ["instrumentality", "instr"]
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_runtime_dependency 'claide', '~> 1.0.2'
25
+ spec.add_runtime_dependency 'colorize', '~> 0.8.1'
26
+ spec.add_runtime_dependency 'simctl', '~> 1.6.2'
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.15"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+ end
@@ -0,0 +1,9 @@
1
+ require 'instrumentality/version'
2
+ require 'instrumentality/command'
3
+ require 'claide'
4
+
5
+ module Instrumentality
6
+ def self.run
7
+ Command.run(ARGV)
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ require 'claide'
2
+ require 'instrumentality/version'
3
+
4
+ module Instrumentality
5
+ class Command < CLAide::Command
6
+ require 'instrumentality/command/benchmark'
7
+ require 'instrumentality/command/file_activity'
8
+ require 'instrumentality/command/generate_header'
9
+ require 'instrumentality/command/custom'
10
+
11
+ self.abstract_command = true
12
+ self.command = 'instr'
13
+ self.version = Instrumentality::VERSION
14
+ self.description = <<-DESC
15
+ Command line interface to profiling tools for iOS development
16
+ DESC
17
+ end
18
+ end
@@ -0,0 +1,61 @@
1
+ require 'instrumentality/command'
2
+ require 'instrumentality/finder'
3
+ require 'instrumentality/profiler'
4
+ require 'instrumentality/constants'
5
+ require 'ostruct'
6
+
7
+ module Instrumentality
8
+ class Benchmark < Command
9
+ def self.options
10
+ [
11
+ ['--workspace=path/to/name.xcworkspace', 'If not set, Instr will try to find one'],
12
+ ['--project=path/to/name.xcodeproj', 'If not set and workspace search failed, Instr will try to find one'],
13
+ ['--scheme=name', 'If not set, Instr will use the process name'],
14
+ ['--server-port=port_number', 'If not set, Instr will use 8080'],
15
+ ['--interactive', 'If set, Instr will not attempt to run an Xcode scheme, but instead attach DTrace directly'],
16
+ ['--experimental', "Don't use it if you don't know what it does"],
17
+ ].concat(super)
18
+ end
19
+
20
+ self.arguments = [
21
+ CLAide::Argument.new("process", true),
22
+ ]
23
+
24
+ self.summary = <<-DESC
25
+ Runs a benchmark against a specific iOS process. Requires static probes.
26
+ DESC
27
+
28
+ def initialize(argv)
29
+ @process = argv.shift_argument
30
+ @workspace = argv.option('workspace') || Finder.find_workspace
31
+ @project = argv.option('project')
32
+ @project ||= Finder.find_project if @workspace.nil?
33
+ @scheme = argv.option('scheme') || @process
34
+ @server_port = argv.option('server-port') || Constants::DEFAULT_SERVER_PORT
35
+ @interactive = argv.flag?('interactive', false)
36
+ @experimental = argv.flag?('experimental', false)
37
+ super
38
+ end
39
+
40
+ def validate!
41
+ super
42
+
43
+ help! 'A process name is required' unless @process
44
+ return if @interactive
45
+ help! 'Xcode workspace or project files not found' if @workspace.nil? && @project.nil?
46
+ end
47
+
48
+ def run
49
+ config = OpenStruct.new({'script' => Constants::BENCHMARK_SCRIPT,
50
+ 'process' => @process,
51
+ 'workspace' => @workspace,
52
+ 'project' => @project,
53
+ 'scheme' => @scheme,
54
+ 'server_port' => @server_port,
55
+ 'interactive' => @interactive,
56
+ 'experimental' => @experimental})
57
+ profiler = Profiler.new(config, @verbose)
58
+ profiler.profile
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,62 @@
1
+ require 'instrumentality/command'
2
+ require 'instrumentality/finder'
3
+ require 'instrumentality/profiler'
4
+ require 'instrumentality/constants'
5
+ require 'ostruct'
6
+
7
+ module Instrumentality
8
+ class Custom < Command
9
+ def self.options
10
+ [
11
+ ['--workspace=path/to/name.xcworkspace', 'If not set, Instr will try to find one'],
12
+ ['--project=path/to/name.xcodeproj', 'If not set and workspace search failed, Instr will try to find one'],
13
+ ['--scheme=name', 'If not set, Instr will use the process name'],
14
+ ['--server-port=port_number', 'If not set, Instr will use 8080'],
15
+ ['--interactive', 'If set, Instr will not attempt to run an Xcode scheme, but instead attach DTrace directly'],
16
+ ].concat(super)
17
+ end
18
+
19
+ self.arguments = [
20
+ CLAide::Argument.new("process", true),
21
+ CLAide::Argument.new("script", true),
22
+ ]
23
+
24
+ self.summary = <<-DESC
25
+ Runs a DTrace script against a specific iOS process.
26
+ DESC
27
+
28
+ def initialize(argv)
29
+ @process = argv.shift_argument
30
+ @script = argv.shift_argument
31
+ @workspace = argv.option('workspace') || Finder.find_workspace
32
+ @project = argv.option('project')
33
+ @project ||= Finder.find_project if @workspace.nil?
34
+ @scheme = argv.option('scheme') || @process
35
+ @server_port = argv.option('server-port') || Constants::DEFAULT_SERVER_PORT
36
+ @interactive = argv.flag?('interactive', false)
37
+ super
38
+ end
39
+
40
+ def validate!
41
+ super
42
+
43
+ help! 'A process name is required' unless @process
44
+ help! 'A valid script path is required' unless File.exist?(@script)
45
+ return if @interactive
46
+ help! 'Xcode workspace or project files not found' if @workspace.nil? && @project.nil?
47
+ end
48
+
49
+ def run
50
+ config = OpenStruct.new({'script' => @script,
51
+ 'process' => @process,
52
+ 'workspace' => @workspace,
53
+ 'project' => @project,
54
+ 'scheme' => @scheme,
55
+ 'server_port' => @server_port,
56
+ 'interactive' => @interactive,
57
+ 'experimental' => false})
58
+ profiler = Profiler.new(config, @verbose)
59
+ profiler.profile
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,61 @@
1
+ require 'instrumentality/command'
2
+ require 'instrumentality/finder'
3
+ require 'instrumentality/profiler'
4
+ require 'instrumentality/constants'
5
+ require 'ostruct'
6
+
7
+ module Instrumentality
8
+ class FileActivity < Command
9
+ def self.options
10
+ [
11
+ ['--workspace=path/to/name.xcworkspace', 'If not set, Instr will try to find one'],
12
+ ['--project=path/to/name.xcodeproj', 'If not set and workspace search failed, Instr will try to find one'],
13
+ ['--scheme=name', 'If not set, Instr will use the process name'],
14
+ ['--server-port=port_number', 'If not set, Instr will use 8080'],
15
+ ['--interactive', 'If set, Instr will not attempt to run an Xcode scheme, but instead attach DTrace directly'],
16
+ ['--experimental', "Don't use it if you don't know what it does"],
17
+ ].concat(super)
18
+ end
19
+
20
+ self.arguments = [
21
+ CLAide::Argument.new("process", true),
22
+ ]
23
+
24
+ self.summary = <<-DESC
25
+ Prints files read or write by a specific iOS process.
26
+ DESC
27
+
28
+ def initialize(argv)
29
+ @process = argv.shift_argument
30
+ @workspace = argv.option('workspace') || Finder.find_workspace
31
+ @project = argv.option('project')
32
+ @project ||= Finder.find_project if @workspace.nil?
33
+ @scheme = argv.option('scheme') || @process
34
+ @server_port = argv.option('server-port') || Constants::DEFAULT_SERVER_PORT
35
+ @interactive = argv.flag?('interactive', false)
36
+ @experimental = argv.flag?('experimental', false)
37
+ super
38
+ end
39
+
40
+ def validate!
41
+ super
42
+
43
+ help! 'A process name is required' unless @process
44
+ return if @interactive
45
+ help! 'Xcode workspace or project files not found' if @workspace.nil? && @project.nil?
46
+ end
47
+
48
+ def run
49
+ config = OpenStruct.new({'script' => Constants::FILE_ACTIVITY_SCRIPT,
50
+ 'process' => @process,
51
+ 'workspace' => @workspace,
52
+ 'project' => @project,
53
+ 'scheme' => @scheme,
54
+ 'server_port' => @server_port,
55
+ 'interactive' => @interactive,
56
+ 'experimental' => @experimental})
57
+ profiler = Profiler.new(config, @verbose)
58
+ profiler.profile
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,35 @@
1
+ require 'instrumentality/command'
2
+ require 'instrumentality/header_generator'
3
+
4
+ module Instrumentality
5
+ class GenerateHeader < Command
6
+ def self.options
7
+ [
8
+ ].concat(super)
9
+ end
10
+
11
+ self.arguments = [
12
+ CLAide::Argument.new("instrument", true),
13
+ ]
14
+
15
+ self.summary = <<-DESC
16
+ Generates Objective-C header for instrument.
17
+ DESC
18
+
19
+ def initialize(argv)
20
+ @instrument = argv.shift_argument
21
+ super
22
+ end
23
+
24
+ def validate!
25
+ super
26
+
27
+ help! 'An instrument name is required' unless @instrument
28
+ end
29
+
30
+ def run
31
+ header_generator = HeaderGenerator.new(@instrument, @verbose)
32
+ header_generator.generate
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ module Instrumentality
2
+ module Constants
3
+ TIMEOUT = 60
4
+ OUTPUT_PREFIX = "[Instr]".freeze
5
+ BENCHMARK_SCRIPT = "benchmark.d".freeze
6
+ FILE_ACTIVITY_SCRIPT = "file_activity.d".freeze
7
+ DTRACE_OUTPUT = "dtrace_output".freeze
8
+ TRACE_SCRIPT_DELIMITER = ":".freeze
9
+ RESPONSE_CODE = "200".freeze
10
+ SUCCESS = "true".freeze
11
+ JTL_REPORT = "instr.jtl".freeze
12
+ DEFAULT_RUNTIME = "latest".freeze
13
+ DEFAULT_DEVICE = "iPhone 6".freeze
14
+ DEFAULT_SERVER_PORT = 8080
15
+ EXPERIMENTAL = "experimental_".freeze
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ require 'instrumentality/constants'
2
+ require 'claide'
3
+ require 'colorize'
4
+ require 'timeout'
5
+
6
+ module Instrumentality
7
+ class Executor
8
+ class ExecutorError < StandardError; include CLAide::InformativeError; end
9
+
10
+ def self.execute(cmd, verbose = false)
11
+ puts "#{Constants::OUTPUT_PREFIX} Executing command: #{cmd}"
12
+ if verbose
13
+ system(cmd)
14
+ else
15
+ `#{cmd}`
16
+ end
17
+ status = $?.exitstatus
18
+ raise ExecutorError, "Previous command execution failed with status: #{status}".red if status != 0
19
+ end
20
+
21
+ def self.execute_async(cmd)
22
+ spawn("#{cmd}").tap do |pid|
23
+ puts "#{Constants::OUTPUT_PREFIX} Spawned process (PID: #{pid}) from command: #{cmd}"
24
+ end
25
+ end
26
+
27
+ def self.timeout(process, seconds = Constants::TIMEOUT)
28
+ pid = ""
29
+ Timeout.timeout(seconds) do
30
+ loop do
31
+ pid = `ps aux | grep #{process}.app | grep -v grep | awk '{print $2}'`.strip
32
+ break if pid != ""
33
+ end
34
+ end
35
+ pid.tap do |pid|
36
+ puts "#{Constants::OUTPUT_PREFIX} Found process #{process} with PID: #{pid}"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ module Instrumentality
2
+ class Finder
3
+ def self.find_workspace
4
+ find('*.xcworkspace', 1).first
5
+ end
6
+
7
+ def self.find_project
8
+ find('*.xcodeproj', 1).first
9
+ end
10
+
11
+ def self.find_xctestrun(location)
12
+ find('*.xctestrun', 0, location).first
13
+ end
14
+
15
+ def self.path_for_script(name)
16
+ return name if File.exist?(name)
17
+ File.expand_path("../scripts/#{name}", __FILE__)
18
+ end
19
+
20
+ def self.path_for_header(name)
21
+ File.expand_path("../headers/#{name}", __FILE__)
22
+ end
23
+
24
+ def self.find(name, depth = 0, location = '.')
25
+ cmd = %W[find #{location} -name '#{name}']
26
+ cmd += %W[-maxdepth #{depth}] if depth > 0
27
+ `#{cmd.join(' ')}`.split("\n")
28
+ end
29
+
30
+ private_class_method :find
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ require 'instrumentality/executor'
2
+ require 'instrumentality/finder'
3
+ require 'instrumentality/constants'
4
+
5
+ module Instrumentality
6
+ class HeaderGenerator
7
+ attr_reader :instrument, :verbose
8
+
9
+ def initialize(instrument, verbose)
10
+ @instrument = instrument
11
+ @verbose = verbose
12
+ end
13
+
14
+ def generate
15
+ dtrace_cmd = %w[dtrace]
16
+ dtrace_cmd += %W[-h -s #{Finder.path_for_header(Constants::BENCHMARK_SCRIPT)}]
17
+ cmd = dtrace_cmd.join(' ')
18
+ Executor.execute(cmd, verbose)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ provider instr {
2
+ probe benchmark_begin(char *);
3
+ probe benchmark_end(char *);
4
+ };
@@ -0,0 +1,130 @@
1
+ require 'instrumentality/executor'
2
+ require 'instrumentality/finder'
3
+ require 'instrumentality/constants'
4
+ require 'instrumentality/simctl'
5
+ require 'net/http'
6
+ require 'uri'
7
+
8
+ module Instrumentality
9
+ class Profiler
10
+ class ProfilerInterruptError < StandardError; include CLAide::InformativeError; end
11
+ class ProfilerError < StandardError; include CLAide::InformativeError; end
12
+ attr_reader :config, :script_path, :verbose, :xcodebuild_pid, :app_pid, :dtrace_pid
13
+
14
+ def initialize(config, verbose)
15
+ @config = config
16
+ @verbose = verbose
17
+ script = config.script.dup
18
+ script.prepend(Constants::EXPERIMENTAL) if @config.experimental
19
+ @script_path = Finder.path_for_script(script)
20
+ raise ProfilerError, "This intrument doesn't support --experimental flag".red unless File.exist?(script_path)
21
+ end
22
+
23
+ def profile
24
+ return interactive if @config.interactive
25
+ current_directory = Dir.pwd
26
+ Dir.mktmpdir do |tmpdir|
27
+ compile(current_directory, tmpdir)
28
+ Simctl.execute_with_simulator_ready(Constants::DEFAULT_RUNTIME, Constants::DEFAULT_DEVICE) do |device|
29
+ run_tests(tmpdir, device)
30
+ find_app_pid
31
+ notify_server
32
+ attach_dtrace(tmpdir)
33
+ wait
34
+ end
35
+ process_dtrace_output(current_directory, tmpdir)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def interactive()
42
+ find_app_pid
43
+ attach_dtrace('.')
44
+ stream_dtrace_output
45
+ end
46
+
47
+ def compile(current_directory, temporary_directory)
48
+ xcodebuild_cmd = %w[xcodebuild]
49
+ xcodebuild_cmd += if config.workspace
50
+ %W[-workspace #{current_directory}/#{config.workspace}]
51
+ else
52
+ %W[-project #{current_directory}/#{config.project}]
53
+ end
54
+ xcodebuild_cmd += %W[-scheme #{config.scheme}]
55
+ xcodebuild_cmd += %w[-sdk iphonesimulator]
56
+ xcodebuild_cmd += %W[-derivedDataPath #{temporary_directory}/derivedData]
57
+ xcodebuild_cmd += %w[clean build-for-testing]
58
+ cmd = xcodebuild_cmd.join(' ')
59
+ Executor.execute(cmd, verbose)
60
+ end
61
+
62
+ def run_tests(temporary_directory, device)
63
+ xctestrun = Finder.find_xctestrun(temporary_directory)
64
+ xcodebuild_cmd = %w[xcodebuild]
65
+ xcodebuild_cmd += %W[-xctestrun #{xctestrun}]
66
+ xcodebuild_cmd += %W[-destination 'platform=iOS Simulator,id=#{device.udid}']
67
+ xcodebuild_cmd += %w[test-without-building]
68
+ cmd = xcodebuild_cmd.join(' ')
69
+ @xcodebuild_pid = Executor.execute_async(cmd)
70
+ end
71
+
72
+ def find_app_pid
73
+ @app_pid = Executor.timeout(config.process)
74
+ end
75
+
76
+ def notify_server
77
+ uri = URI.parse("http://localhost:#{config.server_port}/")
78
+ http = Net::HTTP.new(uri.host, uri.port)
79
+ http.request(Net::HTTP::Get.new(uri.request_uri))
80
+ rescue
81
+ retry
82
+ end
83
+
84
+ def attach_dtrace(temporary_directory)
85
+ dtrace_cmd = %w[sudo dtrace]
86
+ dtrace_cmd += %W[-q -s #{script_path}]
87
+ dtrace_cmd += %W[-p #{app_pid}]
88
+ dtrace_cmd += %W[> #{temporary_directory}/#{Constants::DTRACE_OUTPUT}]
89
+ cmd = dtrace_cmd.join(' ')
90
+ @dtrace_pid = Executor.execute_async(cmd)
91
+ end
92
+
93
+ def wait
94
+ Process.wait(xcodebuild_pid)
95
+ Process.kill("QUIT", dtrace_pid)
96
+ end
97
+
98
+ def process_dtrace_output(current_directory, temporary_directory)
99
+ report = "timeStamp,elapsed,label,responseCode,success\n"
100
+ epoch = Time.now.to_i
101
+ File.readlines("#{temporary_directory}/#{Constants::DTRACE_OUTPUT}").each do |line|
102
+ to_parse = line.strip
103
+ next if to_parse.empty?
104
+ values = to_parse.split(Constants::TRACE_SCRIPT_DELIMITER)
105
+ view_controller = values[0]
106
+ elapsed = values[1].to_f / 1000000
107
+ report += "#{epoch},%.0f,#{view_controller},#{Constants::RESPONSE_CODE},#{Constants::SUCCESS}\n" % elapsed
108
+ end
109
+ File.write("#{current_directory}/#{Constants::JTL_REPORT}", report)
110
+ end
111
+
112
+ def stream_dtrace_output
113
+ output_file = Constants::DTRACE_OUTPUT
114
+ FileUtils.rm_rf(output_file) if File.exist? output_file
115
+ touch_cmd = %W[touch "#{output_file}"]
116
+ cmd = touch_cmd.join(' ')
117
+ Executor.execute(cmd, verbose)
118
+ stream = IO.new(IO.sysopen(output_file))
119
+ puts "#{Constants::OUTPUT_PREFIX} Started interactive session. #{"Press CTRL+C to finish.".blue}"
120
+ begin
121
+ loop do
122
+ output = stream.gets
123
+ puts output unless output.nil?
124
+ end
125
+ rescue SignalException => e
126
+ raise ProfilerInterruptError, "Interactive session ended. Full output at ./#{output_file}".green
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,14 @@
1
+ instr$target:::benchmark_begin
2
+ {
3
+ time = timestamp;
4
+ tag = copyinstr(arg0);
5
+ tag_times[tag] = time;
6
+ }
7
+
8
+ instr$target:::benchmark_end
9
+ / tag_times[copyinstr(arg0)] != 0 /
10
+ {
11
+ tag = copyinstr(arg0);
12
+ time = (timestamp - tag_times[tag]);
13
+ printf("%s:%d\n", tag, time);
14
+ }
@@ -0,0 +1,12 @@
1
+ syscall::open*:entry
2
+ / pid == $target /
3
+ {
4
+ printf("%s file opened by %s\n", copyinstr(arg0), execname);
5
+ }
6
+
7
+ syscall::read*:entry,syscall::write*:entry
8
+ / pid == $target /
9
+ {
10
+ printf("%s file %s by %s\n", fds[arg0].fi_pathname, probefunc, execname)
11
+ }
12
+
@@ -0,0 +1,5 @@
1
+ syscall::read*:entry,syscall::write*:entry
2
+ / pid == $target /
3
+ {
4
+ printf("%s file %s by %s\n", fds[arg0].fi_pathname, probefunc, execname)
5
+ }
@@ -0,0 +1,47 @@
1
+ require 'instrumentality/constants'
2
+ require 'simctl'
3
+
4
+ module Instrumentality
5
+ class Simctl
6
+ def self.execute_with_simulator_ready(runtime, type)
7
+ device = create_device(runtime, type)
8
+ device.launch
9
+ device.wait(Constants::TIMEOUT) do |d|
10
+ d.state == :booted && d.ready?
11
+ end
12
+ begin
13
+ yield device
14
+ rescue StandardError => error
15
+ throw error
16
+ ensure
17
+ delete_device(device)
18
+ end
19
+ end
20
+
21
+ def self.create_device(runtime, type)
22
+ runtime = if runtime.eql? 'latest'
23
+ SimCtl::Runtime.latest('ios')
24
+ else
25
+ SimCtl.runtime(name: runtime)
26
+ end
27
+ device_type = SimCtl.devicetype(name: type)
28
+ device_name = "#{type}-instr"
29
+ SimCtl.reset_device(device_name, device_type, runtime)
30
+ end
31
+
32
+ private_class_method :create_device
33
+
34
+ def self.delete_device(device)
35
+ if device.state != :shutdown
36
+ device.shutdown
37
+ device.kill
38
+ device.wait do |d|
39
+ d.state == :shutdown
40
+ end
41
+ end
42
+ device.delete
43
+ end
44
+
45
+ private_class_method :delete_device
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ module Instrumentality
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: instrumentality
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Renzo Crisóstomo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: claide
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: colorize
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.8.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.8.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: simctl
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.6.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.15'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.15'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: Command line interface to profiling tools for iOS development
98
+ email:
99
+ - renzo.crisostomo@here.com
100
+ executables:
101
+ - instrumentality
102
+ - instr
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - ".rspec"
108
+ - ".travis.yml"
109
+ - Gemfile
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - bin/instr
114
+ - bin/instrumentality
115
+ - instrumentality.gemspec
116
+ - lib/instrumentality.rb
117
+ - lib/instrumentality/command.rb
118
+ - lib/instrumentality/command/benchmark.rb
119
+ - lib/instrumentality/command/custom.rb
120
+ - lib/instrumentality/command/file_activity.rb
121
+ - lib/instrumentality/command/generate_header.rb
122
+ - lib/instrumentality/constants.rb
123
+ - lib/instrumentality/executor.rb
124
+ - lib/instrumentality/finder.rb
125
+ - lib/instrumentality/header_generator.rb
126
+ - lib/instrumentality/headers/benchmark.d
127
+ - lib/instrumentality/profiler.rb
128
+ - lib/instrumentality/scripts/benchmark.d
129
+ - lib/instrumentality/scripts/experimental_file_activity.d
130
+ - lib/instrumentality/scripts/file_activity.d
131
+ - lib/instrumentality/simctl.rb
132
+ - lib/instrumentality/version.rb
133
+ homepage: https://github.com/Ruenzuo/instrumentality
134
+ licenses:
135
+ - MIT
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 2.4.5.2
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Command line interface to profiling tools for iOS development
157
+ test_files: []
158
+ has_rdoc: