Add basic profiling
This commit is contained in:
parent
47aaf7ad0b
commit
e60050ed17
2 changed files with 179 additions and 0 deletions
|
@ -0,0 +1,57 @@
|
||||||
|
# Copyright 2019 Robert Bosch GmbH
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Module for profile data model."""
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from . import DataModel
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileDataModel(DataModel):
|
||||||
|
"""Container to model pre-processed profiling data for analysis."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Constructor."""
|
||||||
|
self.times = pd.DataFrame(columns=[
|
||||||
|
'tid',
|
||||||
|
'depth',
|
||||||
|
'function_name',
|
||||||
|
'start_timestamp',
|
||||||
|
'duration',
|
||||||
|
])
|
||||||
|
|
||||||
|
def add_duration(
|
||||||
|
self,
|
||||||
|
tid: int,
|
||||||
|
depth: int,
|
||||||
|
function_name: str,
|
||||||
|
start_timestamp: int,
|
||||||
|
duration: int,
|
||||||
|
) -> None:
|
||||||
|
data = {
|
||||||
|
'tid': tid,
|
||||||
|
'depth': depth,
|
||||||
|
'function_name': function_name,
|
||||||
|
'start_timestamp': start_timestamp,
|
||||||
|
'duration': duration,
|
||||||
|
}
|
||||||
|
self.times = self.times.append(data, ignore_index=True)
|
||||||
|
|
||||||
|
def print_model(self) -> None:
|
||||||
|
"""Debug method to print every contained df."""
|
||||||
|
print('====================PROFILE DATA MODEL====================')
|
||||||
|
tail = 20
|
||||||
|
print(f'Times (tail={tail}):\n{self.times.tail(tail).to_string()}')
|
||||||
|
print('==========================================================')
|
122
tracetools_analysis/tracetools_analysis/processor/profile.py
Normal file
122
tracetools_analysis/tracetools_analysis/processor/profile.py
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
# Copyright 2019 Robert Bosch GmbH
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Module for profile events processing."""
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
from typing import Tuple
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from tracetools_read.utils import get_field
|
||||||
|
|
||||||
|
from .handler import EventHandler
|
||||||
|
from .handler import EventMetadata
|
||||||
|
|
||||||
|
from ..data_model.profile import ProfileDataModel
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileProcessor(EventHandler):
|
||||||
|
"""
|
||||||
|
Processor that extracts profiling information.
|
||||||
|
|
||||||
|
It uses the following events:
|
||||||
|
* lttng_ust_cyg_profile_fast:func_entry
|
||||||
|
* lttng_ust_cyg_profile_fast:func_exit
|
||||||
|
"""
|
||||||
|
|
||||||
|
FUNCTIONS = {
|
||||||
|
'get_next_ready_executable': [],
|
||||||
|
'wait_for_work': [
|
||||||
|
'collect_entities',
|
||||||
|
'add_handles_to_wait_set',
|
||||||
|
'rmw_wait',
|
||||||
|
'remove_null_handles',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
functions: Dict[str, List[str]] = FUNCTIONS
|
||||||
|
) -> None:
|
||||||
|
handler_map = {
|
||||||
|
'lttng_ust_cyg_profile_fast:func_entry':
|
||||||
|
self._handle_function_entry,
|
||||||
|
'lttng_ust_cyg_profile_fast:func_exit':
|
||||||
|
self._handle_function_exit,
|
||||||
|
}
|
||||||
|
super().__init__(handler_map)
|
||||||
|
|
||||||
|
self._data = ProfileDataModel()
|
||||||
|
self.functions = functions
|
||||||
|
|
||||||
|
# Temporary buffers
|
||||||
|
# tid ->
|
||||||
|
# (list of functions currently executing (ordered by relative depth),
|
||||||
|
# start timestamp of the function)
|
||||||
|
self._current_funcs: Dict[int, List[Tuple[str, int]]] = defaultdict(list)
|
||||||
|
|
||||||
|
# TODO get debug_info from babeltrace for
|
||||||
|
# lttng_ust_cyg_profile_fast:func_entry events
|
||||||
|
# (or resolve { address -> function } name another way)
|
||||||
|
self.address_to_func = {
|
||||||
|
int('0x7F6CD676CDB4', 16): 'get_next_ready_executable',
|
||||||
|
int('0x7F6CD676BC54', 16): 'wait_for_work',
|
||||||
|
int('0x7F6CD678D0F8', 16): 'collect_entities',
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_data_model(self) -> ProfileDataModel:
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
def _handle_function_entry(
|
||||||
|
self, event: Dict, metadata: EventMetadata
|
||||||
|
) -> None:
|
||||||
|
function_name = self._get_function_name(event)
|
||||||
|
assert function_name is not None, f'cannot resolve function name for event: {event}'
|
||||||
|
# Push function data to stack
|
||||||
|
self._current_funcs[metadata.tid].append(
|
||||||
|
(metadata.timestamp, function_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _handle_function_exit(
|
||||||
|
self, event: Dict, metadata: EventMetadata
|
||||||
|
) -> None:
|
||||||
|
# Pop function data from stack
|
||||||
|
tid = metadata.tid
|
||||||
|
tid_functions = self._current_funcs[tid]
|
||||||
|
function_depth = len(tid_functions) - 1
|
||||||
|
(start_timestamp, start_function_name) = tid_functions.pop()
|
||||||
|
# Add to data model
|
||||||
|
duration = metadata.timestamp - start_timestamp
|
||||||
|
self._data.add_duration(
|
||||||
|
tid,
|
||||||
|
function_depth,
|
||||||
|
start_function_name,
|
||||||
|
start_timestamp,
|
||||||
|
duration
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_function_name(
|
||||||
|
self, event: Dict
|
||||||
|
) -> str:
|
||||||
|
address = get_field(event, 'addr')
|
||||||
|
return self._resolve_function_address(address)
|
||||||
|
# return address
|
||||||
|
|
||||||
|
def _resolve_function_address(
|
||||||
|
self, address: int
|
||||||
|
) -> Union[str, None]:
|
||||||
|
# TODO get from trace/binaries
|
||||||
|
return self.address_to_func.get(address, None)
|
Loading…
Add table
Add a link
Reference in a new issue