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