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