Class: RunLoop::Instruments

Inherits:
Object
  • Object
show all
Defined in:
lib/run_loop/instruments.rb

Overview

Note:

All instruments commands are run in the context of ‘xcrun`.

A class for interacting with the instruments command-line tool

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#xcodeObject (readonly)

Returns the value of attribute xcode.



10
11
12
# File 'lib/run_loop/instruments.rb', line 10

def xcode
  @xcode
end

Instance Method Details

#instruments_app_running?Boolean

Is the Instruments.app running?

If the Instruments.app is running, the instruments command line tool cannot take control of applications.

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
# File 'lib/run_loop/instruments.rb', line 77

def instruments_app_running?
  ps_output = `ps x -o pid,comm | grep Instruments.app | grep -v grep`.strip
  if ps_output[/Instruments\.app/, 0]
    true
  else
    false
  end
end

#instruments_pids(&block) ⇒ Array<Integer>

Note:

The ‘block` parameter is included for legacy API and will be deprecated. Replace your existing calls with with .each or .map. The block argument makes this method hard to mock.

Returns an Array of instruments process ids.

Returns:

  • (Array<Integer>)

    An array of instruments process ids.



32
33
34
35
36
37
38
39
40
41
# File 'lib/run_loop/instruments.rb', line 32

def instruments_pids(&block)
  pids = pids_from_ps_output
  if block_given?
    pids.each do |pid|
      block.call(pid)
    end
  else
    pids
  end
end

#instruments_running?Boolean

Are there any instruments processes running?

Returns:

  • (Boolean)

    True if there is are any instruments processes running.



45
46
47
# File 'lib/run_loop/instruments.rb', line 45

def instruments_running?
  instruments_pids.count > 0
end

#kill_instruments(xcode = RunLoop::Xcode.new) ⇒ Object

Send a kill signal to any running ‘instruments` processes.

Only one instruments process can be running at any one time.

Parameters:



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/run_loop/instruments.rb', line 55

def kill_instruments(xcode = RunLoop::Xcode.new)
  if xcode.is_a?(RunLoop::XCTools)
    RunLoop.deprecated('1.5.0',
                     %q(
RunLoop::XCTools has been replaced with RunLoop::Xcode.
Please update your sources to pass an instance of RunLoop::Xcode))
  end

  kill_signal = kill_signal(xcode)
  instruments_pids.each do |pid|
    terminator = RunLoop::ProcessTerminator.new(pid, kill_signal, 'instruments')
    unless terminator.kill_process
      terminator = RunLoop::ProcessTerminator.new(pid, 'KILL', 'instruments')
      terminator.kill_process
    end
  end
end

#physical_devicesArray<RunLoop::Device>

Returns an array the available physical devices.

Returns:



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/run_loop/instruments.rb', line 165

def physical_devices
  @instruments_physical_devices ||= lambda do
    execute_command(['-s', 'devices']) do |stdout, stderr, _|
      filter_stderr_spam(stderr)
      all = stdout.read.chomp.split("\n")
      valid = all.select do |device|
        device =~ DEVICE_UDID_REGEX
      end
      valid.map do |device|
        udid = device[DEVICE_UDID_REGEX, 0]
        version = device[VERSION_REGEX, 0]
        name = device.split('(').first.strip
        RunLoop::Device.new(name, version, udid)
      end
    end
  end.call
end

#simulatorsArray<RunLoop::Device>

Returns an array of the available simulators.

**Xcode 5.1**

  • iPad Retina - Simulator - iOS 7.1

**Xcode 6**

  • iPad Retina (8.3 Simulator) [EA79555F-ADB4-4D75-930C-A745EAC8FA8B]

**Xcode 7**

  • iPhone 6 (9.0) [3EDC9C6E-3096-48BF-BCEC-7A5CAF8AA706]

  • iPhone 6 (9.0) + Apple Watch - 38mm (2.0) [EE3C200C-69BA-4816-A087-0457C5FCEDA0]

Returns:



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/run_loop/instruments.rb', line 196

def simulators
  @instruments_simulators ||= lambda do
    execute_command(['-s', 'devices']) do |stdout, stderr, _|
      filter_stderr_spam(stderr)
      lines = stdout.read.chomp.split("\n")
      lines.map do |line|
        stripped = line.strip
        if line_is_simulator?(stripped) &&
              !line_is_simulator_paired_with_watch?(stripped)

          version = stripped[VERSION_REGEX, 0]

          if line_is_xcode5_simulator?(stripped)
            name = line
            udid = line
          else
            name = stripped.split('(').first.strip
            udid = line[CORE_SIMULATOR_UDID_REGEX, 0]
          end

          RunLoop::Device.new(name, version, udid)
        else
          nil
        end
      end.compact
    end
  end.call
end

#spawn(automation_template, options, log_file) ⇒ Integer

TODO:

Do I need to enumerate the launch options in the docs?

TODO:

Should this raise errors?

TODO:

Is this jruby compatible?

Spawn a new instruments process in the context of ‘xcrun` and detach.

Parameters:

  • automation_template (String)

    The template instruments will use when launching the application.

  • options (Hash)

    The launch options.

  • log_file (String)

    The file to log to.

Returns:

  • (Integer)

    Returns the process id of the instruments process.



96
97
98
99
100
101
102
103
# File 'lib/run_loop/instruments.rb', line 96

def spawn(automation_template, options, log_file)
  splat_args = spawn_arguments(automation_template, options)
  logger = options[:logger]
  RunLoop::Logging.log_debug(logger, "xcrun #{splat_args.join(' ')} >& #{log_file}")
  pid = Process.spawn('xcrun', *splat_args, {:out => log_file, :err => log_file})
  Process.detach(pid)
  pid.to_i
end

#templatesArray<String>

Returns an array of Instruments.app templates.

Depending on the Xcode version Instruments.app templates will either be:

  • A full path to the template. # Xcode 5 and Xcode > 5 betas

  • The name of a template. # Xcode >= 6 (non beta)

**Maintainers!** The rules above are important and explain why we can’t simply filter by ‘~= /tracetemplate/`.

Templates that users have saved will always be full paths - regardless of the Xcode version.

Returns:

  • (Array<String>)

    Instruments.app templates.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/run_loop/instruments.rb', line 130

def templates
  @instruments_templates ||= lambda do
    if xcode.version_gte_6?
      execute_command(['-s', 'templates']) do |stdout, stderr, _|
        filter_stderr_spam(stderr)
        stdout.read.chomp.split("\n").map do |elm|
          stripped = elm.strip.tr('"', '')
          if stripped == '' || stripped == 'Known Templates:'
            nil
          else
            stripped
          end
        end.compact
      end
    elsif xcode.version_gte_51?
      execute_command(['-s', 'templates']) do |stdout, stderr, _|
        err = stderr.read
        if !err.nil? || err != ''
          $stderr.puts stderr.read
        end

        stdout.read.strip.split("\n").delete_if do |path|
          not path =~ /tracetemplate/
        end.map { |elm| elm.strip }
      end
    else
      raise "Xcode version '#{xcode.version}' is not supported."
    end
  end.call
end

#versionRunLoop::Version

Returns the instruments version.

Returns:



107
108
109
110
111
112
113
114
# File 'lib/run_loop/instruments.rb', line 107

def version
  @instruments_version ||= lambda do
    execute_command([]) do |_, stderr, _|
      version_str = stderr.read[VERSION_REGEX, 0]
      RunLoop::Version.new(version_str)
    end
  end.call
end