• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6import signal
7import subprocess
8import sys
9import tempfile
10
11from telemetry.internal.platform import profiler
12from telemetry.internal.platform.profiler import android_prebuilt_profiler_helper
13
14_TCP_DUMP_BASE_OPTS = ['-i', 'any', '-p', '-s', '0', '-w']
15
16
17class _TCPDumpProfilerAndroid(object):
18  """An internal class to collect TCP dumps on android.
19
20  This profiler uses pre-built binaries from AOSP.
21  See more details in prebuilt/android/README.txt.
22  """
23
24  _DEVICE_DUMP_FILE = '/sdcard/tcpdump_profiles/capture.pcap'
25
26  def __init__(self, device, output_path):
27    self._device = device
28    self._output_path = output_path
29    self._device.RunShellCommand(
30        ['mkdir', '-p', os.path.dirname(self._DEVICE_DUMP_FILE)],
31        check_return=True)
32    self._proc = subprocess.Popen(
33        [self._device.adb.GetAdbPath(),
34         '-s', self._device.adb.GetDeviceSerial(),
35         'shell', android_prebuilt_profiler_helper.GetDevicePath('tcpdump')] +
36         _TCP_DUMP_BASE_OPTS +
37         [self._DEVICE_DUMP_FILE])
38
39  def CollectProfile(self):
40    tcpdump_pid = self._device.GetPids('tcpdump')
41    if not tcpdump_pid or not 'tcpdump' in tcpdump_pid:
42      raise Exception('Unable to find TCPDump. Check your device is rooted '
43          'and tcpdump is installed at ' +
44          android_prebuilt_profiler_helper.GetDevicePath('tcpdump'))
45    if len(tcpdump_pid['tcpdump']) > 1:
46      raise Exception(
47          'At most one instance of process tcpdump expected but found pids: '
48          '%s' % tcpdump_pid)
49    tcpdump_pid = int(tcpdump_pid['tcpdump'][0])
50    self._device.RunShellCommand(
51        ['kill', '-term', str(tcpdump_pid)], check_return=True)
52    self._proc.terminate()
53    host_dump = os.path.join(self._output_path,
54                             os.path.basename(self._DEVICE_DUMP_FILE))
55    self._device.PullFile(self._DEVICE_DUMP_FILE, host_dump)
56    print 'TCP dump available at: %s ' % host_dump
57    print 'Use Wireshark to open it.'
58    return host_dump
59
60
61class _TCPDumpProfilerLinux(object):
62  """An internal class to collect TCP dumps on linux desktop."""
63
64  _DUMP_FILE = 'capture.pcap'
65
66  def __init__(self, output_path):
67    if not os.path.exists(output_path):
68      os.makedirs(output_path)
69    self._dump_file = os.path.join(output_path, self._DUMP_FILE)
70    self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
71    try:
72      self._proc = subprocess.Popen(
73          ['tcpdump'] + _TCP_DUMP_BASE_OPTS + [self._dump_file],
74          stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
75    except OSError as e:
76      raise Exception('Unable to execute TCPDump, please check your '
77          'installation. ' + str(e))
78
79  def CollectProfile(self):
80    self._proc.send_signal(signal.SIGINT)
81    exit_code = self._proc.wait()
82    try:
83      if exit_code:
84        raise Exception(
85            'tcpdump failed with exit code %d. Output:\n%s' %
86            (exit_code, self._GetStdOut()))
87    finally:
88      self._tmp_output_file.close()
89    print 'TCP dump available at: ', self._dump_file
90    print 'Use Wireshark to open it.'
91    return self._dump_file
92
93  def _GetStdOut(self):
94    self._tmp_output_file.flush()
95    try:
96      with open(self._tmp_output_file.name) as f:
97        return f.read()
98    except IOError:
99      return ''
100
101
102class TCPDumpProfiler(profiler.Profiler):
103  """A Factory to instantiate the platform-specific profiler."""
104  def __init__(self, browser_backend, platform_backend, output_path, state):
105    super(TCPDumpProfiler, self).__init__(
106        browser_backend, platform_backend, output_path, state)
107    if platform_backend.GetOSName() == 'android':
108      android_prebuilt_profiler_helper.InstallOnDevice(
109          browser_backend.device, 'tcpdump')
110      self._platform_profiler = _TCPDumpProfilerAndroid(
111          browser_backend.device, output_path)
112    else:
113      self._platform_profiler = _TCPDumpProfilerLinux(output_path)
114
115  @classmethod
116  def name(cls):
117    return 'tcpdump'
118
119  @classmethod
120  def is_supported(cls, browser_type):
121    if browser_type.startswith('cros'):
122      return False
123    if sys.platform.startswith('linux'):
124      return True
125    return browser_type.startswith('android')
126
127  def CollectProfile(self):
128    return self._platform_profiler.CollectProfile()
129