#!/usr/bin/python # -*- coding: utf-8 -*- # Lionel GAUTHIER # This script is a downgrade of a python script written for the openair stack, generating sequence diagrams charts, displaying # messages exchanged between LTE access stratum protocol entities. # This script needs mscgen tool (http://www.mcternan.me.uk/mscgen/). # # The aim of this script is to collect some traces from oaisim stack and generate a mscgen script, then generate a sequence diagram # image (png or jpeg) based on the scripts generated. # An example of how it can be invoqued: msc_gen.py /tmp/msc_log.txt /tmp/msc_log2.txt msc_logx.txt are input text files, # They/It are/is generated by the execution of executables in linux user space. May be it is better to operate on log files 'grepped' with MSC: # ./my_exe.exe | grep \[MSC > /tmp/msc_log1.txt # Now, examples of how to generate a mscgen trace in a C executable: # -First you have to declare the instance(s) of the protocol(s) that will send or receive messages. # This is done with the following line: # [MSC_NEW][system time][protocol_name=instance_name] # examples: # printf("[MSC_NEW][%s][MIH-F=%s]\n", getTimeStamp4Log(), g_mihf_id); for declaring a new instance of a protocol entity of type MIH-F # with an instance name g_mihf_id (of type char*) # printf("[MSC_NEW][%s][NAS=%s]\n", getTimeStamp4Log(), "nas"); other example. # This declaration of instances of protocols is mandatory to for this script to work. # # -Then for tracing a message you have to log a line following this format: # [MSC_MSG][system time][source instance_name][--- message_name --->][destination instance_name] # Note: # ---> means a message that has been received correctly by destination # ---x means a message lost by destination # example # NOTICE("[MSC_MSG][%s][%s][--- Link_Register.indication\\n%s ---x][%s]\n",getTimeStamp4Log(),g_link_id,g_msc_gen_buf,g_mihf_id); # import sys import subprocess import re import socket import datetime import os.path from datetime import date MSCGEN_OUTPUT_TYPE = "png" MAX_MESSAGES_PER_PAGE = 36 SYSTEM_FRAME_NUMBER_STR = 'FRAME' MODULE_STR = 'MOD' RADIO_BEARER_STR = 'RB' MSC_NEW_STR = '[MSC_NEW]' MSC_MSG_STR = '[MSC_MSG]' MSC_NBOX_STR = '[MSC_NBOX]' MSC_BOX_STR = '[MSC_BOX]' MSC_ABOX_STR = '[MSC_ABOX]' MSC_RBOX_STR = '[MSC_RBOX]' # This dic is filled as follow : g_entities_dic[protocol_instance_name].append(protocol_name) # where protocol_name can be any of the following MI-USER, MIH-F, RAL, LINK-SAP g_entities_dic = {} g_reverse_entities_dic = {} # g_messages is filled with dictionnaries: # message_dic['entity_src'] = protocol_entity_src # message_dic['entity_dst'] = protocol_entity_dest # message_dic['msg'] = message # message_dic['line_color'] = g_display_color[entity_name] # message_dic['text_color'] = g_display_color[entity_name] # message_dic['time'] = system_frame_number # message_dic['seq_no'] = sequence_number_generator() # g_messages.append(message_dic) g_messages = [] g_messages_dic = {} # g_display_order_dic is a dictionnary where the order of the display of entities sharing the same module identifier is hardcoded #MAC and PHY valus have to be above num max radio bearer value #g_display_order_dic = {'MIH-USER': 0, 'MIH-F': 1, 'RAL': 3, 'NAS': 4} g_display_order_dic = {'NAS-UE': 0, 'AS': 1, 'NAS-MME': 3} # Display color of messages of sending entities g_display_color = {'IP': '\"teal\"', 'RRC_UE': '\"red\"', 'MIH-USER': '\"black\"', 'MIH-F': '\"black\"', 'RAL': '\"black\"', 'RAL_M': '\"black\"', 'RLC_UM': '\"navy\"', 'RLC_TM': '\"navy\"', 'NAS': '\"black\"', 'MAC_eNB': '\"indigo\"', 'PHY': '\"purple\"', 'NAS-UE': '\"black\"', 'NAS-MME': '\"black\"', 'AS': '\"black\"'} g_final_display_order_list = [] g_sequence_generator = 0 def sequence_number_generator(): global g_sequence_generator l_seq = g_sequence_generator g_sequence_generator = g_sequence_generator + 1 return l_seq def parse_log_file_for_discovering_protocol_entities(filename): global g_entities_dic global g_entities global g_messages global g_final_display_order_list #open TXT file that contain OAI filtered traces for mscgen fhandle = open(filename, 'r') fcontent = fhandle.read() fhandle.close() # split file content in lines lines = fcontent.splitlines() for line in lines: system_time = 'unknown' # if line is a trace of the creation of a new protocol instance if MSC_NEW_STR in line: partition = line.rpartition(MSC_NEW_STR) msc_log_string = partition[2] #print (" %s " % msc_log_string) partition = msc_log_string.split('[') print ("\n\n %s \n" % partition) #if len(partition) == 2: item = partition[1] item = item.strip() item = item.strip(']') system_time = item.split(' ')[-1] print ("NEW system_time %s " % system_time) item = partition[2] item = item.strip() item = item.strip(']') protocol_entity_type = item.split('=')[0] protocol_entity_name = item.split('=')[-1] print ("NEW protocol_entity_type %s " % protocol_entity_type) print ("NEW protocol_entity_name %s " % protocol_entity_name) if protocol_entity_name not in g_entities_dic: g_entities_dic[protocol_entity_name] = protocol_entity_type if protocol_entity_type not in g_reverse_entities_dic: g_reverse_entities_dic[protocol_entity_type] = protocol_entity_name else: entity_name_list = g_reverse_entities_dic[protocol_entity_type] entity_name_list.append(protocol_entity_name) g_reverse_entities_dic[protocol_entity_type] = entity_name_list #print (" g_entities_dic[%d][%d].append(%s)" % (module_id_int, radio_bearer_id_int, entity_name_src)) print (" g_entities_dic= %s\n\n" % (g_entities_dic)) print (" g_reverse_entities_dic= %s\n\n" % (g_reverse_entities_dic)) #g_entities.append(protocol_entity_type) #print (" %s \n" % protocol_entity) def parse_log_file(filename): global g_entities_dic global g_entities global g_messages global g_final_display_order_list #open TXT file that contain OAI filtered traces for mscgen fhandle = open(filename, 'r') fcontent = fhandle.read() fhandle.close() # split file content in lines lines = fcontent.splitlines() for line in lines: system_time = 'unknown' message = 'unknown' entity_name_src = 'unknown' entity_name_dest = 'unknown' # if line is a trace of the creation of a new protocol instance if MSC_MSG_STR in line: partition = line.strip().rpartition(MSC_MSG_STR) msc_log_string = partition[2].strip() print (" ++++ %s " % msc_log_string) partition = msc_log_string.split('[') print (" ++++ %s " % partition) #if len(partition) == 9: message_dic = {} system_time = partition[1].strip().split(' ')[-1].strip(']') message_dic['display_time'] = system_time seconds_int = int(system_time.split(':')[0]) micro_seconds_int = int(system_time.split(':')[-1]) system_time_int = seconds_int*1000000 + micro_seconds_int system_time=str(system_time_int) print ('system_time %s' % system_time) entity_name_src = partition[2].strip().split(' ')[-1].strip(']') print ('entity_name_src %s' % entity_name_src) message = partition[3].strip().strip(']').strip() if (message[-1] == 'x') or (message[-1] == 'X'): message_dic['arc'] = '-x' elif message[-1] == '>': message_dic['arc'] = '=>' message = message.strip('<').strip('>').strip('x').strip('X').strip('-').strip('=').strip() print ('message %s' % message) entity_name_dest = partition[4].strip().split(' ')[-1].strip(']') print ('entity_name_dest %s' % entity_name_dest) message_dic['entity_src'] = entity_name_src message_dic['entity_dst'] = entity_name_dest message_dic['msg'] = message message_dic['line_color'] = g_display_color[g_entities_dic[entity_name_src]] message_dic['text_color'] = g_display_color[g_entities_dic[entity_name_src]] message_dic['time'] = system_time #message_dic['seq_no'] = sequence_number_generator() print ('%s\n\n' % message_dic) g_messages_dic[system_time_int] = message_dic #g_messages.append(message_dic) #TODO !!! if MSC_RBOX_STR in line: partition = line.strip().rpartition(MSC_RBOX_STR) msc_log_string = partition[2].strip() print (" ==== %s " % msc_log_string) partition = msc_log_string.split('[') print (" ==== %s " % partition) #TODO !!! def generate_sorted_message_list(): first_time_stamp = 0 for system_time_int in sorted(g_messages_dic.iterkeys()): if first_time_stamp == 0: first_time_stamp = system_time_int message_dic = g_messages_dic[system_time_int] message_dic['seq_no'] = sequence_number_generator() message_dic['display_time'] = str(system_time_int - first_time_stamp) g_messages.append(message_dic) def generate_sorted_entity_display_list(): module_display_order_dic = {} for entity_name in sorted(g_entities_dic.iterkeys()): module_display_order_dic[g_display_order_dic[g_entities_dic[entity_name]]] = entity_name print("------------------------------------") print(" %s " % (module_display_order_dic)) for display_priority in sorted(module_display_order_dic.iterkeys()): g_final_display_order_list.append(module_display_order_dic[display_priority]) def msc_chart_write_header(fileP): global g_final_display_order_list fileP.write("msc {\n") fileP.write("width = \"2048\";\n") entity_line_list_str = '' for entity in g_final_display_order_list: entity_line_list_str = entity_line_list_str + ' ' + entity + ',' entity_line_list_str = entity_line_list_str.rstrip().strip(',') fileP.write(" %s;" % (entity_line_list_str)) def msc_chart_write_footer(fileP): fileP.write("\n}\n") def msc_chart_generate(file_nameP): global MSCGEN_OUTPUT_TYPE command_line = "mscgen -T " + MSCGEN_OUTPUT_TYPE + " -i " + file_nameP + "; chmod 777 " +file_nameP + ";" print("Command Line: %s " % (command_line)) fi,fo,fe=os.popen3(command_line) for i in fe.readlines(): print "error:",i def get_nem_file_descriptor(): global g_base_file_name global g_page_index l_file_name = g_base_file_name + str(g_page_index)+'.txt' l_file = open(l_file_name, "w", 0777) return l_file ###### MAIN START HERE ################# num_args = len(sys.argv) if num_args < 2: # the program name and the arguments # stop the program and print an error message sys.exit("Must provide at least one file to parse") for i in range(1,num_args): print sys.argv[i] for i in range(1,num_args): parse_log_file_for_discovering_protocol_entities(sys.argv[i]) for i in range(1,num_args): parse_log_file(sys.argv[i]) generate_sorted_message_list() generate_sorted_entity_display_list() g_page_index = 0 g_message_index = 0 g_message_index_in_current_page = 0 g_now = datetime.datetime.now() g_now_formated = g_now.strftime("%Y-%m-%d_%H.%M.%S") g_currentdir = os.curdir g_resultdir = os.path.join(g_currentdir, g_now_formated) os.mkdir(g_resultdir, 0777) os.chdir(g_resultdir) g_base_file_name = 'mih_mscgen_page_' g_file = get_nem_file_descriptor() msc_chart_write_header(g_file) for message in g_messages: if 'msg' in message: print ('message %s' % message) g_file.write(" %s%s%s [ label = \"(%d) T%s %s\", linecolour=%s , textcolour=%s ] ;\n" % (message['entity_src'], message['arc'], message['entity_dst'], message['seq_no'], message['display_time'], message['msg'], message['line_color'], message['text_color'])) elif 'box' in message: g_file.write(" %s %s %s [ label = \"%s\", textbgcolour=%s , textcolour=%s ] ;\n" % (message['entity_src'], message['box_type'], message['entity_dst'], message['box'], message['textbg_color'], message['text_color'])) g_message_index = g_message_index + 1 g_message_index_in_current_page = g_message_index_in_current_page + 1 if (g_message_index_in_current_page == MAX_MESSAGES_PER_PAGE): msc_chart_write_footer(g_file) g_file.close() msc_chart_generate(g_file.name) g_page_index = g_page_index + 1 g_message_index_in_current_page = 0 g_file = get_nem_file_descriptor() msc_chart_write_header(g_file) msc_chart_write_footer(g_file) g_file.close() if g_message_index_in_current_page >= 1: msc_chart_generate(g_file.name) else: print("Removing empty seq diagram file: %s " % (g_file.name)) os.remove(g_file.name)