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