1# Copyright 2014 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. 4import os 5import platform 6import stat 7import unittest 8 9from telemetry import decorators 10from telemetry.internal.platform.tracing_agent import chrome_tracing_agent 11from telemetry.internal.platform.tracing_agent import ( 12 chrome_tracing_devtools_manager) 13from telemetry.timeline import tracing_config 14from telemetry.core import cros_interface 15from telemetry.testing import options_for_unittests 16 17 18from devil.android import device_utils 19 20 21class FakeTracingControllerBackend(object): 22 def __init__(self): 23 self.is_tracing_running = False 24 25 26class FakePlatformBackend(object): 27 def __init__(self): 28 self.tracing_controller_backend = FakeTracingControllerBackend() 29 30 def GetOSName(self): 31 return '' 32 33class FakeAndroidPlatformBackend(FakePlatformBackend): 34 def __init__(self): 35 super(FakeAndroidPlatformBackend, self).__init__() 36 devices = device_utils.DeviceUtils.HealthyDevices(None) 37 self.device = devices[0] 38 39 def GetOSName(self): 40 return 'android' 41 42class FakeCrOSPlatformBackend(FakePlatformBackend): 43 def __init__(self): 44 super(FakeCrOSPlatformBackend, self).__init__() 45 remote = options_for_unittests.GetCopy().cros_remote 46 remote_ssh_port = options_for_unittests.GetCopy().cros_remote_ssh_port 47 self.cri = cros_interface.CrOSInterface( 48 remote, remote_ssh_port, 49 options_for_unittests.GetCopy().cros_ssh_identity) 50 51 def GetOSName(self): 52 return 'chromeos' 53 54class FakeDesktopPlatformBackend(FakePlatformBackend): 55 def GetOSName(self): 56 system = platform.system() 57 if system == 'Linux': 58 return 'linux' 59 if system == 'Darwin': 60 return 'mac' 61 if system == 'Windows': 62 return 'win' 63 64 65class FakeContextMap(object): 66 def __init__(self, contexts): 67 self.contexts = contexts 68 69 70class FakeDevtoolsClient(object): 71 def __init__(self, remote_port): 72 self.is_alive = True 73 self.is_tracing_running = False 74 self.remote_port = remote_port 75 self.will_raise_exception_in_stop_tracing = False 76 self.collected = False 77 78 def IsAlive(self): 79 return self.is_alive 80 81 def StartChromeTracing(self, trace_options, timeout=10): 82 del trace_options, timeout # unused 83 self.is_tracing_running = True 84 85 def StopChromeTracing(self): 86 self.is_tracing_running = False 87 if self.will_raise_exception_in_stop_tracing: 88 raise Exception 89 90 def CollectChromeTracingData(self, trace_data_builder, timeout=30): 91 del trace_data_builder # unused 92 del timeout # unused 93 self.collected = True 94 95 def IsChromeTracingSupported(self): 96 return True 97 98 def GetUpdatedInspectableContexts(self): 99 return FakeContextMap([]) 100 101 102class ChromeTracingAgentTest(unittest.TestCase): 103 def setUp(self): 104 self.platform1 = FakePlatformBackend() 105 self.platform2 = FakePlatformBackend() 106 self.platform3 = FakePlatformBackend() 107 108 def StartTracing(self, platform_backend, enable_chrome_trace=True): 109 assert chrome_tracing_agent.ChromeTracingAgent.IsSupported(platform_backend) 110 agent = chrome_tracing_agent.ChromeTracingAgent(platform_backend) 111 config = tracing_config.TracingConfig() 112 config.enable_chrome_trace = enable_chrome_trace 113 config.chrome_trace_config.category_filter.AddIncludedCategory('foo') 114 agent._platform_backend.tracing_controller_backend.is_tracing_running = True 115 agent._test_config = config 116 agent.StartAgentTracing(config, 10) 117 return agent 118 119 def FlushTracing(self, agent): 120 agent.FlushAgentTracing(agent._test_config, 10, None) 121 122 def StopTracing(self, agent): 123 agent._platform_backend.tracing_controller_backend.is_tracing_running = ( 124 False) 125 agent.StopAgentTracing() 126 agent.CollectAgentTraceData(None) 127 128 def testRegisterDevtoolsClient(self): 129 chrome_tracing_devtools_manager.RegisterDevToolsClient( 130 FakeDevtoolsClient(1), self.platform1) 131 chrome_tracing_devtools_manager.RegisterDevToolsClient( 132 FakeDevtoolsClient(2), self.platform1) 133 chrome_tracing_devtools_manager.RegisterDevToolsClient( 134 FakeDevtoolsClient(3), self.platform1) 135 136 tracing_agent_of_platform1 = self.StartTracing(self.platform1) 137 138 chrome_tracing_devtools_manager.RegisterDevToolsClient( 139 FakeDevtoolsClient(4), self.platform1) 140 chrome_tracing_devtools_manager.RegisterDevToolsClient( 141 FakeDevtoolsClient(5), self.platform2) 142 143 self.StopTracing(tracing_agent_of_platform1) 144 chrome_tracing_devtools_manager.RegisterDevToolsClient( 145 FakeDevtoolsClient(6), self.platform1) 146 147 def testIsSupportWithoutStartupTracingSupport(self): 148 self.assertFalse( 149 chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform1)) 150 self.assertFalse( 151 chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform2)) 152 self.assertFalse( 153 chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform3)) 154 155 devtool1 = FakeDevtoolsClient(1) 156 devtool2 = FakeDevtoolsClient(2) 157 chrome_tracing_devtools_manager.RegisterDevToolsClient( 158 devtool1, self.platform1) 159 chrome_tracing_devtools_manager.RegisterDevToolsClient( 160 devtool2, self.platform2) 161 devtool2.is_alive = False 162 163 # Chrome tracing is only supported on platform 1 since only platform 1 has 164 # an alive devtool. 165 self.assertTrue( 166 chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform1)) 167 self.assertFalse( 168 chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform2)) 169 self.assertFalse( 170 chrome_tracing_agent.ChromeTracingAgent.IsSupported(self.platform3)) 171 172 @decorators.Enabled('linux', 'mac', 'win') 173 def testIsSupportOnDesktopPlatform(self): 174 # Chrome tracing is always supported on desktop platforms because of startup 175 # tracing. 176 desktop_platform = FakeDesktopPlatformBackend() 177 self.assertTrue( 178 chrome_tracing_agent.ChromeTracingAgent.IsSupported(desktop_platform)) 179 180 devtool = FakeDevtoolsClient(1) 181 chrome_tracing_devtools_manager.RegisterDevToolsClient( 182 devtool, desktop_platform) 183 self.assertTrue( 184 chrome_tracing_agent.ChromeTracingAgent.IsSupported(desktop_platform)) 185 186 def testStartAndStopTracing(self): 187 devtool1 = FakeDevtoolsClient(1) 188 devtool2 = FakeDevtoolsClient(2) 189 devtool3 = FakeDevtoolsClient(3) 190 devtool4 = FakeDevtoolsClient(2) 191 # Register devtools 1, 2, 3 on platform1 and devtool 4 on platform 2 192 chrome_tracing_devtools_manager.RegisterDevToolsClient( 193 devtool1, self.platform1) 194 chrome_tracing_devtools_manager.RegisterDevToolsClient( 195 devtool2, self.platform1) 196 chrome_tracing_devtools_manager.RegisterDevToolsClient( 197 devtool3, self.platform1) 198 chrome_tracing_devtools_manager.RegisterDevToolsClient( 199 devtool4, self.platform2) 200 devtool2.is_alive = False 201 202 tracing_agent1 = self.StartTracing(self.platform1) 203 with self.assertRaises(chrome_tracing_agent.ChromeTracingStartedError): 204 self.StartTracing(self.platform1) 205 206 self.assertTrue(devtool1.is_tracing_running) 207 self.assertFalse(devtool2.is_tracing_running) 208 self.assertTrue(devtool3.is_tracing_running) 209 # Devtool 4 shouldn't have tracing started although it has the same remote 210 # port as devtool 2 211 self.assertFalse(devtool4.is_tracing_running) 212 213 214 self.assertFalse(devtool1.collected) 215 self.StopTracing(tracing_agent1) 216 self.assertTrue(devtool1.collected) 217 self.assertFalse(devtool1.is_tracing_running) 218 self.assertFalse(devtool2.is_tracing_running) 219 self.assertFalse(devtool3.is_tracing_running) 220 self.assertFalse(devtool4.is_tracing_running) 221 222 # Test that it should be ok to start & stop tracing on platform1 again. 223 tracing_agent1 = self.StartTracing(self.platform1) 224 self.StopTracing(tracing_agent1) 225 226 tracing_agent2 = self.StartTracing(self.platform2) 227 self.assertTrue(devtool4.is_tracing_running) 228 self.assertFalse(devtool4.collected) 229 self.StopTracing(tracing_agent2) 230 self.assertFalse(devtool4.is_tracing_running) 231 self.assertTrue(devtool4.collected) 232 233 def testFlushTracing(self): 234 devtool1 = FakeDevtoolsClient(1) 235 devtool2 = FakeDevtoolsClient(2) 236 devtool3 = FakeDevtoolsClient(3) 237 devtool4 = FakeDevtoolsClient(2) 238 239 # Register devtools 1, 2, 3 on platform1 and devtool 4 on platform 2. 240 chrome_tracing_devtools_manager.RegisterDevToolsClient( 241 devtool1, self.platform1) 242 chrome_tracing_devtools_manager.RegisterDevToolsClient( 243 devtool2, self.platform1) 244 chrome_tracing_devtools_manager.RegisterDevToolsClient( 245 devtool3, self.platform1) 246 chrome_tracing_devtools_manager.RegisterDevToolsClient( 247 devtool4, self.platform2) 248 devtool2.is_alive = False 249 250 tracing_agent1 = self.StartTracing(self.platform1) 251 252 self.assertTrue(devtool1.is_tracing_running) 253 self.assertFalse(devtool2.is_tracing_running) 254 self.assertTrue(devtool3.is_tracing_running) 255 # Devtool 4 shouldn't have tracing started although it has the same remote 256 # port as devtool 2. 257 self.assertFalse(devtool4.is_tracing_running) 258 259 for _ in xrange(5): 260 self.FlushTracing(tracing_agent1) 261 self.assertTrue(devtool1.is_tracing_running) 262 self.assertFalse(devtool2.is_tracing_running) 263 self.assertTrue(devtool3.is_tracing_running) 264 self.assertFalse(devtool4.is_tracing_running) 265 266 self.StopTracing(tracing_agent1) 267 self.assertFalse(devtool1.is_tracing_running) 268 self.assertFalse(devtool2.is_tracing_running) 269 self.assertFalse(devtool3.is_tracing_running) 270 self.assertFalse(devtool4.is_tracing_running) 271 272 # Test that it is ok to start, flush & stop tracing on platform1 again. 273 tracing_agent1 = self.StartTracing(self.platform1) 274 self.FlushTracing(tracing_agent1) 275 self.StopTracing(tracing_agent1) 276 277 tracing_agent2 = self.StartTracing(self.platform2) 278 self.assertTrue(devtool4.is_tracing_running) 279 self.FlushTracing(tracing_agent2) 280 self.assertTrue(devtool4.is_tracing_running) 281 self.StopTracing(tracing_agent2) 282 self.assertFalse(devtool4.is_tracing_running) 283 284 def testExceptionRaisedInStopTracing(self): 285 devtool1 = FakeDevtoolsClient(1) 286 devtool2 = FakeDevtoolsClient(2) 287 # Register devtools 1, 2 on platform 1 288 chrome_tracing_devtools_manager.RegisterDevToolsClient( 289 devtool1, self.platform1) 290 chrome_tracing_devtools_manager.RegisterDevToolsClient( 291 devtool2, self.platform1) 292 tracing_agent1 = self.StartTracing(self.platform1) 293 294 self.assertTrue(devtool1.is_tracing_running) 295 self.assertTrue(devtool2.is_tracing_running) 296 297 devtool1.will_raise_exception_in_stop_tracing = True 298 with self.assertRaises(chrome_tracing_agent.ChromeTracingStoppedError): 299 self.StopTracing(tracing_agent1) 300 # Tracing is stopped on both devtools clients even if there is exception. 301 self.assertIsNone(tracing_agent1.trace_config) 302 self.assertFalse(devtool1.is_tracing_running) 303 self.assertFalse(devtool2.is_tracing_running) 304 305 devtool1.is_alive = False 306 devtool2.is_alive = False 307 # Register devtools 3 on platform 1 should not raise any exception. 308 devtool3 = FakeDevtoolsClient(3) 309 chrome_tracing_devtools_manager.RegisterDevToolsClient( 310 devtool3, self.platform1) 311 312 # Start & Stop tracing on platform 1 should work just fine. 313 tracing_agent2 = self.StartTracing(self.platform1) 314 self.StopTracing(tracing_agent2) 315 316 @decorators.Enabled('android') 317 def testCreateAndRemoveTraceConfigFileOnAndroid(self): 318 platform_backend = FakeAndroidPlatformBackend() 319 agent = chrome_tracing_agent.ChromeTracingAgent(platform_backend) 320 self.assertIsNone(agent.trace_config_file) 321 322 config = tracing_config.TracingConfig() 323 agent._CreateTraceConfigFile(config) 324 self.assertIsNotNone(agent.trace_config_file) 325 self.assertTrue(platform_backend.device.PathExists(agent.trace_config_file)) 326 config_file_str = platform_backend.device.ReadFile(agent.trace_config_file, 327 as_root=True) 328 self.assertEqual(agent._CreateTraceConfigFileString(config), 329 config_file_str.strip()) 330 331 config_file_path = agent.trace_config_file 332 agent._RemoveTraceConfigFile() 333 self.assertFalse(platform_backend.device.PathExists(config_file_path)) 334 self.assertIsNone(agent.trace_config_file) 335 # robust to multiple file removal 336 agent._RemoveTraceConfigFile() 337 self.assertFalse(platform_backend.device.PathExists(config_file_path)) 338 self.assertIsNone(agent.trace_config_file) 339 340 @decorators.Enabled('chromeos') 341 def testCreateAndRemoveTraceConfigFileOnCrOS(self): 342 platform_backend = FakeCrOSPlatformBackend() 343 cri = platform_backend.cri 344 agent = chrome_tracing_agent.ChromeTracingAgent(platform_backend) 345 self.assertIsNone(agent.trace_config_file) 346 347 config = tracing_config.TracingConfig() 348 agent._CreateTraceConfigFile(config) 349 self.assertIsNotNone(agent.trace_config_file) 350 self.assertTrue(cri.FileExistsOnDevice(agent.trace_config_file)) 351 config_file_str = cri.GetFileContents(agent.trace_config_file) 352 self.assertEqual(agent._CreateTraceConfigFileString(config), 353 config_file_str.strip()) 354 355 config_file_path = agent.trace_config_file 356 agent._RemoveTraceConfigFile() 357 self.assertFalse(cri.FileExistsOnDevice(config_file_path)) 358 self.assertIsNone(agent.trace_config_file) 359 # robust to multiple file removal 360 agent._RemoveTraceConfigFile() 361 self.assertFalse(cri.FileExistsOnDevice(config_file_path)) 362 self.assertIsNone(agent.trace_config_file) 363 364 @decorators.Enabled('linux', 'mac', 'win') 365 def testCreateAndRemoveTraceConfigFileOnDesktop(self): 366 platform_backend = FakeDesktopPlatformBackend() 367 agent = chrome_tracing_agent.ChromeTracingAgent(platform_backend) 368 self.assertIsNone(agent.trace_config_file) 369 370 config = tracing_config.TracingConfig() 371 agent._CreateTraceConfigFile(config) 372 self.assertIsNotNone(agent.trace_config_file) 373 self.assertTrue(os.path.exists(agent.trace_config_file)) 374 self.assertTrue(os.stat(agent.trace_config_file).st_mode & stat.S_IROTH) 375 with open(agent.trace_config_file, 'r') as f: 376 config_file_str = f.read() 377 self.assertEqual(agent._CreateTraceConfigFileString(config), 378 config_file_str.strip()) 379 380 config_file_path = agent.trace_config_file 381 agent._RemoveTraceConfigFile() 382 self.assertFalse(os.path.exists(config_file_path)) 383 self.assertIsNone(agent.trace_config_file) 384 # robust to multiple file removal 385 agent._RemoveTraceConfigFile() 386 self.assertFalse(os.path.exists(config_file_path)) 387 self.assertIsNone(agent.trace_config_file) 388