| # Copyright 2023 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| import asyncio | |
| import json | |
| import subprocess | |
| import tempfile | |
| import fidl_fuchsia_tracing as tracing | |
| import fidl_fuchsia_tracing_controller as tracing_controller | |
| import fuchsia_async_extension | |
| from mobly import asserts, test_runner | |
| from mobly_controller import fuchsia_device | |
| from fidl import AsyncSocket | |
| TRACE2JSON = "tracing_runtime_deps/trace2json" | |
| class FuchsiaControllerTests(fuchsia_async_extension.AsyncBaseTestClass): | |
| async def setup_class(self) -> None: | |
| self.fuchsia_devices: list[ | |
| fuchsia_device.FuchsiaDevice | |
| ] = await self.register_controller(fuchsia_device) | |
| self.device = self.fuchsia_devices[0] | |
| self.device.set_ctx(self) | |
| async def test_fuchsia_device_get_known_categories(self) -> None: | |
| """Verifies that kernel:vm is an existing category for tracing on the device.""" | |
| if self.device.ctx is None: | |
| raise ValueError(f"Device: {self.device.target} has no context") | |
| ch = self.device.ctx.connect_device_proxy( | |
| "core/trace_manager", tracing_controller.ProvisionerMarker | |
| ) | |
| controller = tracing_controller.ProvisionerClient(ch) | |
| try: | |
| response = (await controller.get_known_categories()).unwrap() | |
| except AssertionError as e: | |
| raise AssertionError("Error retrieving known categories") from e | |
| categories = response.categories | |
| found_kernel_category = False | |
| for category in categories: | |
| if category.name == "kernel:vm": | |
| found_kernel_category = True | |
| break | |
| asserts.assert_true( | |
| found_kernel_category, | |
| msg="Was not able to find 'kernel.vm' category in known output", | |
| ) | |
| async def test_fuchsia_device_tracing_start_stop(self) -> None: | |
| """Does a simple start and stop of tracing on a device.""" | |
| if self.device.ctx is None: | |
| raise ValueError(f"Device: {self.device.target} has no context") | |
| ch = self.device.ctx.connect_device_proxy( | |
| "core/trace_manager", tracing_controller.ProvisionerMarker | |
| ) | |
| provisioner = tracing_controller.ProvisionerClient(ch) | |
| categories = [ | |
| "blobfs", | |
| "gfx", | |
| "system_metrics", | |
| ] | |
| config = tracing_controller.TraceConfig( | |
| buffer_size_megabytes_hint=4, | |
| categories=categories, | |
| buffering_mode=tracing.BufferingMode.ONESHOT, | |
| ) | |
| _client, server = self.device.ctx.socket_create() | |
| client = AsyncSocket(_client) | |
| client_end, server_end = self.device.channel_create() | |
| provisioner.initialize_tracing( | |
| controller=server_end.take(), config=config, output=server.take() | |
| ) | |
| controller = tracing_controller.SessionClient(client_end) | |
| await controller.start_tracing() | |
| socket_task = asyncio.create_task(client.read_all()) | |
| await asyncio.sleep(10) | |
| try: | |
| stop_tracing_response = ( | |
| await controller.stop_tracing(write_results=True) | |
| ).unwrap() | |
| except AssertionError as e: | |
| raise AssertionError("Error stopping tracing") from e | |
| assert ( | |
| stop_tracing_response.provider_stats is not None | |
| ), "Stop result provider stats should not be None." | |
| asserts.assert_true( | |
| len(stop_tracing_response.provider_stats) > 0, | |
| msg="Stop result provider stats should not be empty.", | |
| ) | |
| # Closing the channel will terminate tracing | |
| controller.close_cleanly() | |
| raw_trace = await socket_task | |
| asserts.assert_equal(type(raw_trace), bytearray) | |
| asserts.assert_true( | |
| len(raw_trace) > 0, msg="Output bytes should not be empty." | |
| ) | |
| with tempfile.NamedTemporaryFile( | |
| mode="wb", suffix=".fxt" | |
| ) as trace_fxt, tempfile.NamedTemporaryFile( | |
| mode="w+", suffix=".json", encoding="utf8" | |
| ) as trace_json: | |
| trace_fxt.write(raw_trace) | |
| trace_fxt.flush() | |
| subprocess.check_call( | |
| [ | |
| TRACE2JSON, | |
| f"--input-file={trace_fxt.name}", | |
| f"--output-file={trace_json.name}", | |
| ] | |
| ) | |
| js_obj = json.load(trace_json) | |
| asserts.assert_true( | |
| js_obj.get("traceEvents") is not None, | |
| "Expected traceEvents to be present", | |
| ) | |
| for trace_event in js_obj["traceEvents"]: | |
| trace_cat = trace_event["cat"] | |
| asserts.assert_true( | |
| trace_cat in categories, | |
| msg=f"Found unexpected category that isn't part of trace: {trace_cat}", | |
| ) | |
| if __name__ == "__main__": | |
| test_runner.main() |