tcpdump_profiler.py revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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.core.platform import profiler
12from telemetry.core.platform.profiler import android_prebuilt_profiler_helper
13
14
15_TCP_DUMP_BASE_OPTS = ['-i', 'any', '-p', '-s', '0', '-w']
16
17
18class _TCPDumpProfilerAndroid(object):
19  """An internal class to collect TCP dumps on android.
20
21  This profiler uses pre-built binaries from AOSP.
22  See more details in prebuilt/android/README.txt.
23  """
24
25  _DEVICE_DUMP_FILE = '/sdcard/tcpdump_profiles/capture.pcap'
26
27  def __init__(self, adb, output_path):
28    self._adb = adb
29    self._output_path = output_path
30    self._adb.RunShellCommand('mkdir -p ' +
31                              os.path.dirname(self._DEVICE_DUMP_FILE))
32    self._proc = subprocess.Popen(
33        ['adb', '-s', self._adb.device(),
34         'shell', android_prebuilt_profiler_helper.GetDevicePath('tcpdump')] +
35         _TCP_DUMP_BASE_OPTS +
36         [self._DEVICE_DUMP_FILE])
37
38  def CollectProfile(self):
39    tcpdump_pid = self._adb.ExtractPid('tcpdump')
40    if not tcpdump_pid or not tcpdump_pid[0]:
41      raise Exception('Unable to find TCPDump. Check your device is rooted '
42          'and tcpdump is installed at ' +
43          android_prebuilt_profiler_helper.GetDevicePath('tcpdump'))
44    self._adb.RunShellCommand('kill -term ' + tcpdump_pid[0])
45    self._proc.terminate()
46    host_dump = os.path.join(self._output_path,
47                             os.path.basename(self._DEVICE_DUMP_FILE))
48    self._adb.Adb().Adb().Pull(self._DEVICE_DUMP_FILE, host_dump)
49    print 'TCP dump available at: %s ' % host_dump
50    print 'Use Wireshark to open it.'
51    return host_dump
52
53
54class _TCPDumpProfilerLinux(object):
55  """An internal class to collect TCP dumps on linux desktop."""
56
57  _DUMP_FILE = 'capture.pcap'
58
59  def __init__(self, output_path):
60    if not os.path.exists(output_path):
61      os.makedirs(output_path)
62    self._dump_file = os.path.join(output_path, self._DUMP_FILE)
63    self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
64    try:
65      self._proc = subprocess.Popen(
66          ['tcpdump'] + _TCP_DUMP_BASE_OPTS + [self._dump_file],
67          stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
68    except OSError as e:
69      raise Exception('Unable to execute TCPDump, please check your '
70          'installation. ' + str(e))
71
72  def CollectProfile(self):
73    self._proc.send_signal(signal.SIGINT)
74    exit_code = self._proc.wait()
75    try:
76      if exit_code:
77        raise Exception(
78            'tcpdump failed with exit code %d. Output:\n%s' %
79            (exit_code, self._GetStdOut()))
80    finally:
81      self._tmp_output_file.close()
82    print 'TCP dump available at: ', self._dump_file
83    print 'Use Wireshark to open it.'
84    return self._dump_file
85
86  def _GetStdOut(self):
87    self._tmp_output_file.flush()
88    try:
89      with open(self._tmp_output_file.name) as f:
90        return f.read()
91    except IOError:
92      return ''
93
94
95class TCPDumpProfiler(profiler.Profiler):
96  """A Factory to instantiate the platform-specific profiler."""
97  def __init__(self, browser_backend, platform_backend, output_path, state):
98    super(TCPDumpProfiler, self).__init__(
99        browser_backend, platform_backend, output_path, state)
100    if platform_backend.GetOSName() == 'android':
101      android_prebuilt_profiler_helper.InstallOnDevice(
102          browser_backend.adb, 'tcpdump')
103      self._platform_profiler = _TCPDumpProfilerAndroid(
104          browser_backend.adb, output_path)
105    else:
106      self._platform_profiler = _TCPDumpProfilerLinux(output_path)
107
108  @classmethod
109  def name(cls):
110    return 'tcpdump'
111
112  @classmethod
113  def is_supported(cls, browser_type):
114    if browser_type.startswith('cros'):
115      return False
116    if sys.platform.startswith('linux'):
117      return True
118    return browser_type.startswith('android')
119
120  def CollectProfile(self):
121    return self._platform_profiler.CollectProfile()
122