instrumentality 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: