Merge branch 'move-convert-if-needed-function-to-loading-submodule' into 'master'

Move input path inspection + optional conversion logic to loading submodule

See merge request micro-ROS/ros_tracing/tracetools_analysis!39
This commit is contained in:
Christophe Bedard 2019-12-29 19:39:20 +00:00
commit 938cc263d5
8 changed files with 150 additions and 105 deletions

View file

@ -11,8 +11,8 @@
"# Get trace data using the provided launch file:\n", "# Get trace data using the provided launch file:\n",
"# $ ros2 launch tracetools_analysis pingpong.launch.py\n", "# $ ros2 launch tracetools_analysis pingpong.launch.py\n",
"# (wait a few seconds, then kill with Ctrl+C)\n", "# (wait a few seconds, then kill with Ctrl+C)\n",
"# AND\n", "#\n",
"# Convert trace data:\n", "# (optional) convert trace data:\n",
"# $ ros2 run tracetools_analysis convert ~/.ros/tracing/pingpong/ust\n", "# $ ros2 run tracetools_analysis convert ~/.ros/tracing/pingpong/ust\n",
"#\n", "#\n",
"# OR\n", "# OR\n",
@ -27,8 +27,8 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"converted_file_path = '~/.ros/tracing/pingpong/ust/converted'\n", "path = '~/.ros/tracing/pingpong/ust'\n",
"#converted_file_path = 'sample_data/converted_pingpong'" "#path = 'sample_data/converted_pingpong'"
] ]
}, },
{ {
@ -67,7 +67,7 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"# Process\n", "# Process\n",
"events = load_file(converted_file_path)\n", "events = load_file(path)\n",
"handler = Ros2Handler.process(events)\n", "handler = Ros2Handler.process(events)\n",
"#handler.data.print_data()" "#handler.data.print_data()"
] ]
@ -215,7 +215,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.6.8" "version": "3.6.9"
} }
}, },
"nbformat": 4, "nbformat": 4,

View file

@ -11,8 +11,8 @@
"# Get trace data using the provided launch file:\n", "# Get trace data using the provided launch file:\n",
"# $ ros2 launch tracetools_analysis memory_usage.launch.py\n", "# $ ros2 launch tracetools_analysis memory_usage.launch.py\n",
"# (wait a few seconds, then kill with Ctrl+C)\n", "# (wait a few seconds, then kill with Ctrl+C)\n",
"# AND\n", "#\n",
"# Convert trace data:\n", "# (optional) convert trace data:\n",
"# $ ros2 trace-analysis convert ~/.ros/tracing/memory-usage" "# $ ros2 trace-analysis convert ~/.ros/tracing/memory-usage"
] ]
}, },
@ -22,7 +22,7 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"converted_file_path = '~/.ros/tracing/memory-usage/converted'" "path = '~/.ros/tracing/memory-usage'"
] ]
}, },
{ {
@ -65,7 +65,7 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"# Process\n", "# Process\n",
"events = load_file(converted_file_path)\n", "events = load_file(path)\n",
"ust_memory_handler = UserspaceMemoryUsageHandler()\n", "ust_memory_handler = UserspaceMemoryUsageHandler()\n",
"kernel_memory_handler = KernelMemoryUsageHandler()\n", "kernel_memory_handler = KernelMemoryUsageHandler()\n",
"ros2_handler = Ros2Handler()\n", "ros2_handler = Ros2Handler()\n",

View file

@ -18,7 +18,7 @@ import shutil
import tempfile import tempfile
import unittest import unittest
from tracetools_analysis.process import inspect_input_path from tracetools_analysis.loading import inspect_input_path
class TestProcessCommand(unittest.TestCase): class TestProcessCommand(unittest.TestCase):

View file

@ -16,17 +16,120 @@
import os import os
import pickle import pickle
import sys
from typing import Dict from typing import Dict
from typing import List from typing import List
from typing import Tuple
from tracetools_read.trace import is_trace_directory
from ..convert import convert
from ..convert import DEFAULT_CONVERT_FILE_NAME
def load_file(file_path: str) -> List[Dict]: def inspect_input_path(
input_path: str,
force_conversion: bool = False,
) -> Tuple[str, bool]:
"""
Check input path for a converted file or a trace directory.
If the input path is a file, it uses it as a converted file.
If the input path is a directory, it checks if there is a "converted" file directly inside it,
otherwise it tries to import the path as a trace directory.
If `force_conversion` is set to `True`, even if a converted file is found, it will ask to
re-create it.
:param input_path: the path to a converted file or trace directory
:param force_conversion: whether to re-create converted file even if it is found
:return:
the path to a converted file (or `None` if could not find),
`True` if the given converted file should be (re-)created, `False` otherwise
"""
input_path = os.path.expanduser(input_path)
converted_file_path = None
# Check if not a file
if not os.path.isfile(input_path):
input_directory = input_path
# Might be a (trace) directory
# Check if there is a converted file under the given directory
prospective_converted_file = os.path.join(input_directory, DEFAULT_CONVERT_FILE_NAME)
if os.path.isfile(prospective_converted_file):
# Use that as the converted input file
converted_file_path = prospective_converted_file
if force_conversion:
print(f'found converted file but will re-create it: {prospective_converted_file}')
return prospective_converted_file, True
else:
print(f'found converted file: {prospective_converted_file}')
return prospective_converted_file, False
else:
# Check if it is a trace directory
# Result could be unexpected because it will look for trace directories recursively
# (e.g. '/' is a valid trace directory if there is at least one trace anywhere)
if is_trace_directory(input_directory):
# Convert trace directory first to create converted file
return prospective_converted_file, True
else:
# We cannot do anything
print(
f'cannot find either a trace directory or a converted file: {input_directory}',
file=sys.stderr)
return None, None
else:
converted_file_path = input_path
if force_conversion:
# It's a file, but re-create it anyway
print(f'found converted file but will re-create it: {converted_file_path}')
return converted_file_path, True
else:
# Simplest use-case: given path is an existing converted file
# No need to print anything
return converted_file_path, False
def convert_if_needed(
input_path: str,
force_conversion: bool = False,
) -> str:
"""
Inspect input path and convert trace directory to file if necessary.
:param input_path: the path to a converted file or trace directory
:param force_conversion: whether to re-create converted file even if it is found
"""
converted_file_path, create_converted_file = inspect_input_path(input_path, force_conversion)
if converted_file_path is None:
return None
# Convert trace directory to file if necessary
if create_converted_file:
input_directory = os.path.dirname(converted_file_path)
input_file_name = os.path.basename(converted_file_path)
convert(input_directory, input_file_name)
return converted_file_path
def load_file(
input_path: str,
do_convert_if_needed: bool = True,
force_conversion: bool = False,
) -> List[Dict]:
""" """
Load file containing converted trace events. Load file containing converted trace events.
:param file_path: the path to the converted file to load :param input_path: the path to a converted file or trace directory
:param do_convert_if_needed: whether to create the converted file if needed (else, let it fail)
:param force_conversion: whether to re-create converted file even if it is found
:return: the list of events read from the file :return: the list of events read from the file
""" """
if do_convert_if_needed or force_conversion:
file_path = convert_if_needed(input_path, force_conversion)
else:
file_path = input_path
events = [] events = []
with open(os.path.expanduser(file_path), 'rb') as f: with open(os.path.expanduser(file_path), 'rb') as f:
p = pickle.Unpickler(f) p = pickle.Unpickler(f)

View file

@ -16,18 +16,12 @@
"""Entrypoint/script to process events from a converted file to build a ROS model.""" """Entrypoint/script to process events from a converted file to build a ROS model."""
import argparse import argparse
import os
import sys
import time import time
from typing import Optional from typing import Optional
from typing import Tuple
from tracetools_analysis.convert import convert
from tracetools_analysis.convert import DEFAULT_CONVERT_FILE_NAME
from tracetools_analysis.loading import load_file from tracetools_analysis.loading import load_file
from tracetools_analysis.processor import Processor from tracetools_analysis.processor import Processor
from tracetools_analysis.processor.ros2 import Ros2Handler from tracetools_analysis.processor.ros2 import Ros2Handler
from tracetools_read.trace import is_trace_directory
from . import time_diff_to_str from . import time_diff_to_str
@ -54,67 +48,6 @@ def parse_args():
return parser.parse_args() return parser.parse_args()
def inspect_input_path(
input_path: str,
force_conversion: bool = False,
) -> Tuple[str, bool]:
"""
Check input path for a converted file or a trace directory.
If the input path is a file, it uses it as a converted file.
If the input path is a directory, it checks if there is a "converted" file directly inside it,
otherwise it tries to import the path as a trace directory.
If `force_conversion` is set to `True`, even if a converted file is found, it will ask to
re-create it.
:param input_path: the path to a converted file or trace directory
:param force_conversion: whether to re-creating converted file even if it is found
:return:
the path to a converted file (or `None` if could not find),
`True` if the given converted file should be (re-)created, `False` otherwise
"""
input_path = os.path.expanduser(input_path)
converted_file_path = None
# Check if not a file
if not os.path.isfile(input_path):
input_directory = input_path
# Might be a (trace) directory
# Check if there is a converted file under the given directory
prospective_converted_file = os.path.join(input_directory, DEFAULT_CONVERT_FILE_NAME)
if os.path.isfile(prospective_converted_file):
# Use that as the converted input file
converted_file_path = prospective_converted_file
if force_conversion:
print(f'found converted file but will re-create it: {prospective_converted_file}')
return prospective_converted_file, True
else:
print(f'found converted file: {prospective_converted_file}')
return prospective_converted_file, False
else:
# Check if it is a trace directory
# Result could be unexpected because it will look for trace directories recursively
# (e.g. '/' is a valid trace directory if there is at least one trace anywhere)
if is_trace_directory(input_directory):
# Convert trace directory first to create converted file
return prospective_converted_file, True
else:
# We cannot do anything
print(
f'cannot find either a trace directory or a converted file: {input_directory}',
file=sys.stderr)
return None, None
else:
converted_file_path = input_path
if force_conversion:
# It's a file, but re-create it anyway
print(f'found converted file but will re-create it: {converted_file_path}')
return converted_file_path, True
else:
# Simplest use-case: given path is an existing converted file
print(f'found converted file: {converted_file_path}')
return converted_file_path, False
def process( def process(
input_path: str, input_path: str,
force_conversion: bool = False, force_conversion: bool = False,
@ -127,20 +60,9 @@ def process(
:param force_conversion: whether to re-creating converted file even if it is found :param force_conversion: whether to re-creating converted file even if it is found
:param hide_results: whether to hide results and not print them :param hide_results: whether to hide results and not print them
""" """
converted_file_path, create_converted_file = inspect_input_path(input_path, force_conversion)
if converted_file_path is None:
return 1
# Convert trace directory to file if necessary
if create_converted_file:
input_directory = os.path.dirname(converted_file_path)
input_file_name = os.path.basename(converted_file_path)
convert(input_directory, input_file_name)
start_time = time.time() start_time = time.time()
events = load_file(converted_file_path) events = load_file(input_path, do_convert_if_needed=True, force_conversion=force_conversion)
processor = Processor(Ros2Handler()) processor = Processor(Ros2Handler())
processor.process(events) processor.process(events)

View file

@ -0,0 +1,25 @@
# Copyright 2019 Apex.AI, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
from typing import List
def get_input_path(
argv: List[str] = sys.argv,
) -> str:
if len(argv) < 2:
print('Syntax: [trace directory | converted tracefile]')
sys.exit(1)
return argv[1]

View file

@ -13,14 +13,14 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import sys
import pandas as pd import pandas as pd
from tracetools_analysis.loading import load_file from tracetools_analysis.loading import load_file
from tracetools_analysis.processor.ros2 import Ros2Handler from tracetools_analysis.processor.ros2 import Ros2Handler
from tracetools_analysis.utils.ros2 import Ros2DataModelUtil from tracetools_analysis.utils.ros2 import Ros2DataModelUtil
from . import get_input_path
removals = [ removals = [
'void (', 'rclcpp::', 'std::shared_ptr<', '>', '::msg' 'void (', 'rclcpp::', 'std::shared_ptr<', '>', '::msg'
@ -40,11 +40,9 @@ def format_fn(fname: str):
def main(): def main():
if len(sys.argv) < 2: input_path = get_input_path()
print('Syntax: <tracefile>')
sys.exit(-1)
events = load_file(sys.argv[1]) events = load_file(input_path)
handler = Ros2Handler.process(events) handler = Ros2Handler.process(events)
du = Ros2DataModelUtil(handler.data) du = Ros2DataModelUtil(handler.data)

View file

@ -12,8 +12,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import sys
from tracetools_analysis.loading import load_file from tracetools_analysis.loading import load_file
from tracetools_analysis.processor import Processor from tracetools_analysis.processor import Processor
from tracetools_analysis.processor.memory_usage import KernelMemoryUsageHandler from tracetools_analysis.processor.memory_usage import KernelMemoryUsageHandler
@ -22,14 +20,13 @@ from tracetools_analysis.processor.ros2 import Ros2Handler
from tracetools_analysis.utils.memory_usage import MemoryUsageDataModelUtil from tracetools_analysis.utils.memory_usage import MemoryUsageDataModelUtil
from tracetools_analysis.utils.ros2 import Ros2DataModelUtil from tracetools_analysis.utils.ros2 import Ros2DataModelUtil
from . import get_input_path
def main(): def main():
if len(sys.argv) < 2: input_path = get_input_path()
print('Syntax: <converted tracefile>')
sys.exit(1)
file_path = sys.argv[1]
events = load_file(file_path) events = load_file(input_path)
ust_memory_handler = UserspaceMemoryUsageHandler() ust_memory_handler = UserspaceMemoryUsageHandler()
kernel_memory_handler = KernelMemoryUsageHandler() kernel_memory_handler = KernelMemoryUsageHandler()
ros2_handler = Ros2Handler() ros2_handler = Ros2Handler()