Beispiel #1
0
    def __init__(self, cfg=None):
        if cfg is not None:
            unix_time_off = statistics.median(sofa_time.get_unix_mono_diff()
                                              for i in range(100))
            with open('%s/unix_time_off.txt' % cfg.logdir, 'w') as logfile:
                unix_time_off = sofa_time.get_unix_mono_diff()
                logfile.write(str('%.9lf' % unix_time_off) + '\n')

        if cfg is not None:
            if cfg.ros2_topic_whitelist:
                sofa_print.print_hint("enable ros2 topic whitelist")
            # Not implemented yet
            if cfg.ros2_topic_blacklist:
                sofa_print.print_hint("enable ros2 topic blacklist")
        else:
            cfg = sofa_config.SOFA_Config()

        cflags = []
        if cfg.ros2_topic_whitelist:
            cflags.append('-DWHITELIST=1')
        b = BPF(src_file=os.path.join(
            pathlib.Path(__file__).parent.absolute(), './ebpf_ros2.c'),
                cflags=cflags)

        os.makedirs(os.path.join(cfg.logdir, cfg.ros2logdir), exist_ok=True)

        self.end = multiprocessing.Event()
        self.ready = multiprocessing.Event()
        arg = {'set': self.end, 'config': cfg, 'b': b}
        self.perf_procs = [
            ebpf_ros2_trace_tc_act.trace_tc_act(args=(arg)),
            ebpf_ros2_trace_send.trace_send(args=(arg)),
            ebpf_ros2_trace_recv.trace_recv(args=(arg))
        ]
def print_event(cpu, data, size):
    global ebpf_data
    event = b["events"].event(data)
    data_keys = ['ts', 'comm']
    d = {field:getattr(event, field) for field in data_keys} # a data point in sofa
    d['ts'] = d['ts'] / 1e9 + sofa_time.get_unix_mono_diff()
    ebpf_data.append(d['ts'])
    print(d['ts'])
import threading
import sofa_time
import statistics
#import pandas as pd
import time, sys
import math

serv_addr = '192.168.3.10'
iv_length = 30  # length of the interval to determine time offset
sample_period = 1  # sample period for time offset

if len(sys.argv) > 1:
    serv_addr = sys.argv[1]
    print(sys.argv[1])

unix_mono_diff = sofa_time.get_unix_mono_diff()
time_offset_table = []
time_offset_median = []


class time_offset_from(threading.Thread):
    def __init__(self, serv_addr):
        threading.Thread.__init__(self)
        self.serv_addr = serv_addr
        self.stopped = threading.Event()
        self.start()

    def run(self):
        global time_offset_table
        while not self.stopped.wait(sample_period):
            ts = time.time()
def extract_individual_rosmsg(df_send, df_recv, *df_others):
    """ Return a dictionary with topic name as key and
        a list of ros message as value.
        Structure of return value: {topic_name: {(guid, seqnum): log}}
        where (guid, seqnum) is a msg_id
    """
    # Convert timestamp to unix time
    unix_time_off = statistics.median(sofa_time.get_unix_mono_diff()
                                      for i in range(100))
    for df in (df_send, df_recv, *df_others):
        df['ts'] = df['ts'] + unix_time_off

    # sort by timestamp
    df_send.sort_values(by=['ts'], ignore_index=True)
    df_recv.sort_values(by=['ts'], ignore_index=True)

    # publish side
    gb_send = df_send.groupby('guid')
    all_publishers_log = {guid: log for guid, log in gb_send}

    # subscription side
    gb_recv = df_recv.groupby('guid')
    all_subscriptions_log = {guid: log for guid, log in gb_recv}

    # other logs (assume there's no happen-before relations that needed to be resolved)
    # every dataframe is a dictionary in `other_log_list`
    gb_others = [df_other.groupby('guid') for df_other in df_others]
    other_log_list = [{guid: log
                       for guid, log in gb_other} for gb_other in gb_others]

    # find guids that are in both subsciption and publisher log
    interested_guids = all_subscriptions_log.keys() \
                     & all_publishers_log.keys()

    res = {}
    for guid in interested_guids:
        # get a publisher from log
        df = all_publishers_log[guid]
        df_send_partial = all_publishers_log[guid].copy()
        add_data_calls = df[~pd.isna(
            df['seqnum'])]  # get all non-NaN seqnums in log
        try:
            pubaddr, = pd.unique(df['publisher']).dropna()
            print(pubaddr)
        except ValueError as e:
            print(
                'Find a guid that is not associated with a publisher memory address. Error: '
                + str(e))
            continue
        # print(add_data_calls)

        all_RTPSMsg_idx = ((df_send['func'] == '~RTPSMessageGroup') &
                           (df_send['publisher'] == pubaddr))
        all_RTPSMsgret_idx = ((df_send['func'] == '~RTPSMessageGroup exit') &
                              (df_send['publisher'] == pubaddr))
        all_sendSync_idx = ((df_send['func'] == 'sendSync') &
                            (df_send['publisher'] == pubaddr))
        all_nn_xpack_idx = (df['func'] == 'nn_xpack_send1')
        modified_rows = []
        for idx, add_data_call in add_data_calls.iterrows():
            ts = add_data_call['ts']

            rcl_idx = df.loc[(df['ts'] < ts)
                             & (df['layer'] == 'rcl')]['ts'].idxmax()
            df_send_partial.loc[rcl_idx,
                                'seqnum'] = add_data_call.loc['seqnum']

            # For grouping RTPSMessageGroup function
            try:
                ts_gt = (df_send['ts'] > ts
                         )  # ts greater than that of add_data_call

                RTPSMsg_idx = df_send.loc[ts_gt
                                          & all_RTPSMsg_idx]['ts'].idxmin()
                modified_row = df_send.loc[RTPSMsg_idx]
                modified_row.at['seqnum'] = add_data_call.loc['seqnum']
                modified_row.at['guid'] = guid
                modified_rows.append(modified_row)

                RTPSMsgret_idx = df_send.loc[
                    ts_gt & all_RTPSMsgret_idx]['ts'].idxmin()
                modified_row = df_send.loc[RTPSMsgret_idx]
                modified_row.at['seqnum'] = add_data_call.loc['seqnum']
                modified_row.at['guid'] = guid
                modified_rows.append(modified_row)

                sendSync_idx = df_send.loc[
                    ts_gt & (df_send['ts'] < df_send.loc[RTPSMsgret_idx, 'ts'])
                    & all_sendSync_idx]
                sendSync = sendSync_idx.copy()
                sendSync['seqnum'] = add_data_call.loc['seqnum']
                modified_rows.extend(row for _, row in sendSync.iterrows())
            except ValueError as e:
                pass

            if 'rmw_cyclonedds_cpp' in df['implementation'].values:
                try:
                    df_cls = other_log_list[0][guid]
                    seqnum = add_data_call.loc['seqnum']
                    max_ts = df_cls[(df_cls['layer'] == 'cls_egress') &
                                    (df_cls['seqnum'] == seqnum)]['ts'].max()
                    index = df.loc[(ts < df['ts']) & (df['ts'] < max_ts)
                                   & all_nn_xpack_idx].index
                    df_send_partial.loc[index, 'seqnum'] = seqnum
                except ValueError as e:
                    pass
        df_send_partial = pd.concat(
            [df_send_partial, pd.DataFrame(modified_rows)])

        # get a subscrption from log
        df = all_subscriptions_log[guid]
        df_recv_partial = all_subscriptions_log[guid].copy()
        add_recvchange_calls = df[~pd.isna(
            df['seqnum'])]  # get all not nan seqnums in log

        all_sub = pd.unique(
            df['subscriber'])  # How many subscribers subscribe to this topic?
        subs_map = {
            sub: (df['subscriber'] == sub) &
            (df['func'] == "rmw_take_with_info exit")
            for sub in all_sub
        }
        all_pid = pd.unique(df_recv['pid'])
        pid_maps = {
            pid: (df_recv['pid'] == pid) & (df_recv['func'] == "rmw_wait exit")
            for pid in all_pid
        }
        modified_rows = []
        for idx, add_recvchange_call in add_recvchange_calls.iterrows():
            ts = add_recvchange_call['ts']
            subaddr = add_recvchange_call.at['subscriber']
            seqnum = add_recvchange_call.loc['seqnum']

            # Consider missing `rmw_take_with_info exit` here
            try:
                rmw_take_idx = df.loc[(df['ts'] > ts)
                                      & subs_map[subaddr]]['ts'].idxmin()
                df_recv_partial.at[rmw_take_idx, 'seqnum'] = seqnum

                # TODO: Group by ip port in cls_ingress
                UDPResourceReceive_idx = df.loc[
                    (df['ts'] < ts) & (df['func'] == 'UDPResourceReceive exit')
                    & (df['pid']
                       == add_recvchange_call.at['pid'])]['ts'].idxmax()
                df_recv_partial.at[UDPResourceReceive_idx, 'seqnum'] = seqnum
            except ValueError as e:
                pass
            try:
                # Group rmw_wait exit
                pid = df_recv_partial.at[rmw_take_idx, 'pid']
                rmw_wait_idx = df_recv.loc[
                    (df_recv['ts'] < df_recv_partial.at[rmw_take_idx, 'ts'])
                    & pid_maps[pid]]['ts'].idxmax()
                modified_row = df_recv.loc[rmw_wait_idx]
                modified_row.at['seqnum'] = add_recvchange_call.at['seqnum']
                modified_row.at['guid'] = guid
                modified_rows.append(modified_row)
            except ValueError as e:
                pass
        # Doesn't need to remove duplicates for
        # a = pd.DataFrame(modified_rows)
        # print(a[~a.index.duplicated(keep='first')])
        df_recv_partial = pd.concat(
            [df_recv_partial, pd.DataFrame(modified_rows)])

        # Merge all modified dataframes
        df_merged = df_send_partial.append(df_recv_partial,
                                           ignore_index=True,
                                           sort=False)

        # handle other log files
        for other_log in other_log_list:
            df_other = other_log[guid]
            df_merged = df_merged.append(df_other,
                                         ignore_index=True,
                                         sort=False)

        # Avoid `TypeError: boolean value of NA is ambiguous` when calling groupby()
        df_merged['subscriber'] = df_merged['subscriber'].fillna(np.nan)
        df_merged['guid'] = df_merged['guid'].fillna(np.nan)
        df_merged['seqnum'] = df_merged['seqnum'].fillna(np.nan)
        df_merged.sort_values(by=['ts'], inplace=True)
        gb_merged = df_merged.groupby(['guid', 'seqnum'])

        ros_msgs = {msg_id: log
                    for msg_id, log in gb_merged}  # msg_id: (guid, seqnum)
        # get topic name from log
        topic_name = df_merged['topic_name'].dropna().unique()
        if len(topic_name) > 1:
            raise Exception("More than one topic in a log file")
        topic_name = topic_name[0]

        if topic_name in res:
            res[topic_name] = {**res[topic_name], **ros_msgs}
        else:
            res[topic_name] = ros_msgs
        print('finished parsing ' + topic_name)
    return res