HEX
Server: nginx/1.22.1
System: Linux VM-16-9-centos 3.10.0-1160.99.1.el7.x86_64 #1 SMP Wed Sep 13 14:19:20 UTC 2023 x86_64
User: www (1001)
PHP: 7.3.31
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: //lib64/mft/python_tools/mlxmcg/mlxmcg.py
#--
#                 - Mellanox Confidential and Proprietary -
# 
# Copyright (C) Jan 2013, Mellanox Technologies Ltd.  ALL RIGHTS RESERVED.
# 
# Except as specifically permitted herein, no portion of the information,
# including but not limited to object code and source code, may be reproduced,
# modified, distributed, republished or otherwise exploited in any form or by
# any means for any purpose without the prior written permission of Mellanox
# Technologies Ltd. Use of software subject to the terms and conditions
# detailed in the file "LICENSE.txt".
#--



# built by Oren duer
# modifed by Alaa Barari abarari@asaltech.com 

import sys
import os
import subprocess
import re
from optparse import OptionParser

try :
    import tools_version
except Exception as e:
    print ("-E- could not import : %s" % str(e))
    sys.exit(1)

try :

    def signalHandler():
        print ('\nYou pressed Ctrl+C!')
        sys.exit(1)

    def isLinux():
        linux = 0
        if 'uname' in dir(os):
            uname = os.uname()
            linux = uname[0] == 'Linux'
        return linux
    
    def isRoot():
        if isLinux() and os.geteuid() != 0:
            print ("-E- Permission denied, you need to run this tool as root")
            sys.exit(1)
    
    isRoot()
    
    try:
        MFT_BIN_DIR = os.environ['MFT_BIN_DIR'] + os.sep
    except:
        MFT_BIN_DIR = ""
    
    MCRA_BIN = MFT_BIN_DIR + "mcra"
    WQDUMP_BIN = MFT_BIN_DIR + "wqdump"
    
    if sys.version_info < (2, 4):
        sys.stderr.write("-E- This tool requires python 2.4 or greater\n");
        sys.exit(0)
    
    parser = OptionParser(description="Displays the current multicast groups and flow steering rules configured in the device")
    parser.add_option("-d", "--dev", dest="dev", default='',
                      help="MST device to use, required")
    
    parser.add_option("-f", "--file", dest="file", default='',
                      help="MCG dump file to use (for debug), used as input and there is no need for device")
    
    parser.add_option("-p", "--params", dest="params", default='(64, 32768, 65536)',
                      help="Mcg params, \"(MCG_ENTRY_SIZE, HASH_TABLE_SIZE, MCG_TABLE_SIZE)\", default is (64, 32768, 65536)")
    
    parser.add_option("-q", "--quiet",
                      dest="quiet", default=False, action='store_true',
                      help="Do not print progress messages to stderr")
    
    parser.add_option("-v", "--version",
                      dest="printver", default=False, action='store_true',
                      help="Print tool version")
    
    parser.add_option("-c", "--hopcount",
                      dest="hopcount", default=False, action='store_true',
                      help="add hopCount column")
    
    parser.add_option("-a", "--advanced",
                      dest="advanced", default=False, action='store_true',
                      help="show all rules")
    
    (options, args) = parser.parse_args()
    
    if options.printver == True:
        tools_version.PrintVersionString("mlxmcg", "1.0.18")
        sys.exit(0)
    
    if options.dev == '' and options.file == '':
        print ('-E- --dev argument required')
        sys.exit(1)

    if options.file != '' and options.dev != '':
        print ('-E- Cant use device a long with -f, this is a debug feature that requires no device.')
        sys.exit(1)

    regs = {
        'p0_b0_mcg_size':	{'addr':'0x44240.0:5'},
        'p0_b0_mc_hash_size_mc':{'addr':'0x44240.8:5'},
        'p0_b0_mc_hash_size_uc':{'addr':'0x44240.16:5'},
        'p0_b0_mcg_size_64B'   :{'addr':'0x44240.5:1'},
        'p1_b0_mcg_size':	{'addr':'0x44260.0:5'},
        'p1_b0_mc_hash_size_mc':{'addr':'0x44260.8:5'},
        'p1_b0_mc_hash_size_uc':{'addr':'0x44260.16:5'},
        'p1_b0_mcg_size_64B'   :{'addr':'0x44260.5:1'},
        'log_mc_table_sz':	{'addr':'0x1f388.17:5'},
        'cx3_original_hash_shift': {'addr':'0x44254.24:5'},
    }
    
    tests = [
        ['p0_b0_mcg_size', 'p1_b0_mcg_size'],
        ['p0_b0_mc_hash_size_mc', 'p1_b0_mc_hash_size_mc'],
        ['p0_b0_mc_hash_size_uc', 'p1_b0_mc_hash_size_uc'],
        ['p0_b0_mc_hash_size_mc', 'p0_b0_mc_hash_size_uc']]
    
    mreadcmd = MCRA_BIN + " " + options.dev
    def mread(addr):
        cmd = mreadcmd + ' ' + addr
        process = subprocess.Popen(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
        if process.wait() != 0:
            #print ("-E- Could not access device : %s, cmd : %s,  Error : %s" % (options.dev, cmd, process.stdout.read().strip()))
            print ("-E- Could not access the device: \"%s\" by the command: \"%s\"" % (options.dev, cmd))
            sys.exit(1)
        str = process.stdout.read()
        try :
            return int(str, 16)
        except Exception as e:
            print ("-E- Could not access the device: \"%s\" by the command: \"%s\"" % (options.dev, cmd))
            sys.exit(1)
    
    def calchashsize():
        hash_size = 1 << regs['p0_b0_mc_hash_size_mc']['val']
        if (hash_size < (1 << (regs['cx3_original_hash_shift']['val'] + 1))):
            hash_size = 1 << (regs['cx3_original_hash_shift']['val'] + 1)
        return hash_size
    
    def dumpDevice():
        global MCG_ENTRY_SIZE, HASH_TABLE_SIZE, MCG_TABLE_SIZE
        for reg in regs:
            regs[reg]['val'] = mread(regs[reg]['addr'])
    
        for test in tests:
            if regs[test[0]]['val'] != regs[test[1]]['val']:
                print ("Unsupported MCG configuration. %s (%d) should be equal to %s (%d)" % \
                (test[0], regs[test[0]]['val'],
                 test[1], regs[test[1]]['val']))
    
        if regs['p0_b0_mcg_size_64B']['val']:   
            MCG_ENTRY_SIZE = 64
        else :
            MCG_ENTRY_SIZE = 64 * (1 << regs['p0_b0_mcg_size']['val'])
        HASH_TABLE_SIZE = calchashsize()
        MCG_TABLE_SIZE = (1 << regs['log_mc_table_sz']['val'])
    
    def isSupported(device):
        supported = [0x190, 0x1f5, 0x1f7]
        if "ibdr-" in device or "lid-" in device :
            print ("-E- mlxmcg over inband device is not supported, device : '%s'" % (device))
            sys.exit(1)
        cmd = MCRA_BIN + " " + device + " 0xf0014"
        process  = subprocess.Popen(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
        if process.wait()!= 0:
            print ("-E- Could not access the device: \"%s\" by the command: \"%s\"" % (device, cmd))
            sys.exit(1)
        output = process.communicate()[0]
        try :
            devId = int(output[6:10], 16)
        except Exception as e :
            print("-E- Unexpected output of cmd : %s, output : %s" % (cmd, output))
            sys.exit(1)
        if devId not in supported :
            print ("-E- The given device (%s) is not supported, only ConnectX3/ConnectX3-Pro devices are supported." % (device))
            sys.exit(1)
    
    wqdump_cmd = WQDUMP_BIN + ' -d ' + options.dev + ' --source mcg '
    if options.file == '':
        isSupported(options.dev)
        wqdump_all_cmd = wqdump_cmd + '--dump ALL_QPC '
        dumpDevice()
    else :
        if "win" in sys.platform :
            print ("-E- Dump file option is not supported for Windows")
            sys.exit(1)
        wqdump_all_cmd = 'cat ' + options.file
        (MCG_ENTRY_SIZE, HASH_TABLE_SIZE, MCG_TABLE_SIZE) = eval(options.params)
    
    print ("MCG table size:  %d K entries, Hash size:  %d K entries, Entry size:  %d B" % ((MCG_TABLE_SIZE/1024), (HASH_TABLE_SIZE/1024), MCG_ENTRY_SIZE))
    l4_prot_map = {0:"--", 3:"Other", 5:"UDP", 6:"TCP" }
    progress_resolution = 1024
    factor = int(MCG_ENTRY_SIZE / 64)
    
    
    
    #replaces (val if cond else other_val) syntax which isn't supported in py < 2.5
    def select_val(condition, true_val, false_val):
        if condition:
            return true_val
        return false_val
    
    
    class Mcg:
        fmt     = "%-8s %-4s %-7s %-6s %-4s %-5s %-17s %-15s %-15s  %-17s %-7s %-4s %-5s %-5s %-5s "
        fmt_gid = "%-8s %-4s %-7s %-6s %-61s %-17s %-7s %-4s %-5s %-5s %-5s "
    
        def get_hdr_str(self):
            if options.hopcount or  options.advanced:
                return self.fmt % ('ID', 'Prio', 'Proto', 'DQP', 'Port', 'VLAN', 'MAC', 'SIP', 'DIP', 'I-MAC', 'I-VLAN', 'VNI', 'L4', 'SPort', 'DPort') + "HCount  Next   QPs"
            else :
                return self.fmt % ('ID', 'Prio', 'Proto', 'DQP', 'Port', 'VLAN', 'MAC', 'SIP', 'DIP', 'I-MAC', 'I-VLAN', 'VNI', 'L4', 'SPort', 'DPort') + "   Next  QPs"
        def __str__(self):
            str = ''
            self.uniqueStr = ''
            try:
                str = self.fmt_gid % (
                    "%-8x" % self.reg_id,
                    "%-4x" % self.prio,
                    "%-4s" % self.protocol,
                    select_val(self.dqp_check, "%06x" % self.dqp, '--'),
                    "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" % (
                    (self.gid[3] >> 16) & 0xffff,
                    (self.gid[3]      ) & 0xffff,
                    (self.gid[2] >> 16) & 0xffff,
                    (self.gid[2]      ) & 0xffff,
                    (self.gid[1] >> 16) & 0xffff,
                    (self.gid[1]      ) & 0xffff,
                    (self.gid[0] >> 16) & 0xffff,
                    (self.gid[0] & 0xffff)),
                    "%-17s" % "--",  # I mac
                    "%-5s"  % "--",  # I vlan
                    "%-4s"  % "--",  # VNI
                    l4_prot_map[self.l4_proto],
                    select_val(self.sport_check, "%-5d" % self.sport, '--'),
                    select_val(self.dport_check, "%-5d" % self.dport, '--'))
    
            except:
                vlan = '--'
                inner_vlan = '--'
                if self.vlan_check:
                    if self.vlan_present:
                        vlan = "%-5d" % self.vlan
                    else:
                        vlan = "none"
                if self.inner_vlan_check :
                    if self.inner_vlan_present:
                        inner_vlan = "%-5d" % self.inner_vlan
                
                str = self.fmt % (
                    "%-8x" % self.reg_id,
                    "%-4x" % self.prio,
                    "%-4s" % self.protocol,
                    select_val(self.dqp_check, "%06x" % self.dqp ,'--'),
                    "%1d" % (2 - self.port),
                    "%-5s" % vlan,
                    select_val(self.mac_check, "%02x:%02x:%02x:%02x:%02x:%02x" % (
                                (self.mac & 0xff0000000000) >> 40,
                                (self.mac & 0xff00000000) >> 32,
                                (self.mac & 0xff000000) >> 24,
                                (self.mac & 0xff0000) >> 16,
                                (self.mac & 0xff00) >> 8,
                    (self.mac & 0xff)), '--'),
                    select_val(self.sip_check, "%d.%d.%d.%d" % ((self.sip & 0xff000000) >> 24,
                                (self.sip & 0xff0000) >> 16,
                                (self.sip & 0xff00) >> 8,
                                (self.sip & 0xff)), '--'),
                    
                    select_val(self.dip_check, "%d.%d.%d.%d" % ((self.dip & 0xff000000) >> 24,
                                (self.dip & 0xff0000) >> 16,
                                (self.dip & 0xff00) >> 8,
                                (self.dip & 0xff)), '--'),
                    select_val(self.inner_mac_check, "%02x:%02x:%02x:%02x:%02x:%02x" % (
                                (self.inner_mac & 0xff0000000000) >> 40,
                                (self.inner_mac & 0xff00000000) >> 32,
                                (self.inner_mac & 0xff000000) >> 24,
                                (self.inner_mac & 0xff0000) >> 16,
                                (self.inner_mac & 0xff00) >> 8,
                    (self.inner_mac & 0xff)), '--'),
                    "%-5s" % inner_vlan,
                    select_val(self.inner_vni_check, "%-5s" % self.vni, '--'),
                    l4_prot_map[self.l4_proto],
                    select_val(self.sport_check, "%-5d" % self.sport, '--'),
                    select_val(self.dport_check, "%-5d" % self.dport, '--'))
        
            self.uniqueStr = str
            for qp in self.qps:
                self.uniqueStr = self.uniqueStr + "%x " % qp
    
            return str
    
        def _parse_ipv6(self, mapping):
            self.gid = []
            self.gid.append(\
                mapping['mgi.mac_31_0']   & 0xffffffff)
            self.gid.append(\
                (mapping['mgi.vlan_prio']      & 0x7)         |\
                    (mapping['mgi.vlan_cfi']       & 0x1)    << 3 |\
                    (mapping['mgi.vlan_id']        & 0xfff)  << 4 |\
                    (mapping['mgi.mac_47_32']      & 0xffff) << 16)
            self.gid.append(\
                (mapping['mgi.et_filter_bits'] & 0xffff)       |\
                    (mapping['mgi.mgi_dip_31_24']  & 0xff)   << 16 |\
                    (mapping['mgi.vep']            & 0x7)    << 24 |\
                    (mapping['mgi.port']           & 0x1)    << 27 |\
                    (mapping['mgi.force_loopback'] & 0x1)    << 28 |\
                    (mapping['mgi.et_other_filter']& 0x1)    << 29 |\
                    (mapping['mgi.multicast']      & 0x1)    << 30 |\
                    (mapping['mgi.vlan_present']   & 0x1)    << 31)
            self.gid.append(\
                mapping['mgi.mgi_sip']    & 0xffffffff)
    
            self.l4_proto = mapping['ctl.l4_protocol']
            self.sport_check = mapping['ctl.sport_check']
            self.sport = mapping['ctl.mgi_sport']
            self.dport_check = mapping['ctl.dport_check']
            self.dport = mapping['ctl.mgi_dport']
            self.dqp_check = mapping['ctl.dqp_check']
            self.dqp = mapping['ctl.mgi_dqp_dip_23_0']
    
        def _parse_rules(self, mapping):
    
            self.sip_check = mapping['ctl.sip_check']
            self.sip = mapping['mgi.mgi_sip']
            self.dip_check = mapping['ctl.dip_check']
            self.dip = (mapping['mgi.mgi_dip_31_24'] << 24) + mapping['ctl.mgi_dqp_dip_23_0']
            self.l4_proto = mapping['ctl.l4_protocol']
            self.sport_check = mapping['ctl.sport_check']
            self.sport = mapping['ctl.mgi_sport']
            self.dport_check = mapping['ctl.dport_check']
            self.dport = mapping['ctl.mgi_dport']
            self.dqp_check = mapping['ctl.dqp_check']
            self.dqp = mapping['ctl.mgi_dqp_dip_23_0']
    
        def _parse_tunnel(self, mapping):
            try :
                self.inner_vni_check = mapping['ctl.vni_check']
                self.vni  = mapping['ctl.vni']
                self.inner_vlan_check =  mapping['ctl.inner_vlan_check']
                self.inner_vlan_present = mapping['ctl.inner_vlan_present']
                self.inner_vlan = mapping['ctl.inner_vlan_id']
                self.inner_mac_check = mapping['ctl.inner_mac_check']
                self.inner_mac = (mapping['ctl.inner_dmac_47_32'] << 32 )+ mapping['mgi.inner_dmac_31_0']
            except :
                self._parse_rules(mapping)
    
        def _parse_mapping(self, mapping):
            self.index = mapping['index']
            self.protocol = mapping['ctl.protocol']
    
            protocol = {0 : "IPv6", 1 : "L2_TUNNEL", 2 : "L2", 4 : "IPv4", 6 : "FCoETH", 7 : "all"}
            newName = protocol.get(self.protocol)
            if newName != None :
                self.protocol = newName
    
            if self.protocol == 'IPv6': # ipv6
                self._parse_ipv6(mapping)
            else:
                self.inner_mac_check     = 0
                self.inner_vlan_check    = 0
                self.inner_vlan_present  = 0
                self.inner_vni_check     = 0
                self.vni = "--"
                self.inner_mac = 0
                self.dqp_check = 0
                self.dqp = 0
                self.sip_check = 0
                self.sip = 0
                self.dip_check = 0
                self.dip = 0
                self.sport_check = 0
                self.sport = 0
                self.dport_check = 0
                self.dport = 0
                self.dqp_check = 0
                self.dqp = 0
                self.l4_proto = 0
    
                if self.protocol == 'L2_TUNNEL': # L2_TUNNEL
                    self._parse_tunnel(mapping)
                else : #l2 ....
                    self._parse_rules(mapping)
    
                self.port = mapping['mgi.port']
                self.mac_check = mapping['ctl.mac_check']
                self.mac = (mapping['mgi.mac_47_32'] << 32) + mapping['mgi.mac_31_0']
                self.vlan_check = mapping['ctl.vlan_check']
                self.vlan = mapping['mgi.vlan_id']
                self.vlan_present = mapping['mgi.vlan_present']
    
            if l4_prot_map.get(self.l4_proto) == None:
                self.l4_proto = 3
            self.next = int(mapping['ctl.next_mcg'] / factor)
            try :
                self.prio = ((mapping['member[0].fw_cxt']) +
                             (mapping['member[1].fw_cxt'] << 4) +
                             (mapping['member[2].fw_cxt'] << 8) +
                             (mapping['member[3].fw_cxt'] << 12)
                             )
            except :
                try :
                    self.prio = ((mapping['member[0]'] & 0x0f000000) >> 24) + \
                        ((mapping['member[1]'] & 0x0f000000) >> 20) + \
                        ((mapping['member[2]'] & 0x0f000000) >> 16) + \
                        ((mapping['member[3]'] & 0x0f000000) >> 12)
                except :
                    self.prio = 0
    
            m = 0
            self.qps = []
            self.member_cnt = mapping['ctl.member_cnt']
            while m < self.member_cnt:
                if mapping.get('member_' + str(m) + '_.qpn') != None :
                    self.qps.append(mapping['member_' + str(m) + '_.qpn'])
                elif mapping.get('member[' + str(m) + '].qpn') != None :
                    self.qps.append(mapping['member[' + str(m) + '].qpn'])
                m = m + 1
            try :
                self.reg_id = mapping['member_7_.qpn']
            except :
                self.reg_id = mapping['member[7].qpn']
    
        def parse_wqdump_single_mcg(self, output):
            in_mcg = False
            mapping = {}
            for line in output:
                if line.startswith('----------------------------------Index') or line.startswith('MCG MCG Index'):
                    #m = re.search(r"Index 0x([0-9a-fA-F]*) ", line)
                    m = re.search(r"Index 0x?\s*([0-9a-fA-F]+).*?", line)
                    mapping['index'] = int(int(m.group(1), 16) / factor)
                    in_mcg = True
                    continue
                if line.startswith('--------------------------------------------') or line.startswith('--------------------------'):
                    break
                if in_mcg:
                    words = line.split()
                    key = words[0]
                    try:
                        val = int(words[2], 16)
                    except:
                        val = words[2]
                    mapping[key] = val
    
            if in_mcg:
                self._parse_mapping(mapping)
                return True
            return False
    
        def read_wqdump_single_mcg(self, index):
            index = index * factor
            output = os.popen(wqdump_cmd + '--dump QP --qp ' + str(index))
            return output
    
        def getkey(self):
            m = self
            m.reg_id = 0
            str(m)
            return m.uniqueStr
    
    progress_cnt = 0
    def progress(x):
        if options.quiet:
            return
        global progress_cnt
        if progress_cnt == 0:
            print('Progress: ')
        if (progress_cnt % progress_resolution) == 0:
            print(x)
        progress_cnt += 1
    
    
    output = os.popen(wqdump_all_cmd)
    
    def fill_mcgtable(output, idx, bucket = None):
        global mcgtable
        while True:
            m = Mcg()
            m.hopCount = idx
            if not m.parse_wqdump_single_mcg(output):
                break
            if bucket == None:
                m.bucket = m.index
            else:
                m.bucket = bucket
    
            if m.index < HASH_TABLE_SIZE:
                progress('H')
            else:
                progress('T')
            mcgtable[m.index] = m
    
    mcgtable = dict()
    mcglist = []
    mapMcgList = dict()
    mcgListIndex = 0
    rulescount = dict()
    total = 0
    idx = 0
    
    fill_mcgtable(output, idx)
    while idx < HASH_TABLE_SIZE:
        try:
            m = mcgtable[idx]
        except:
            idx += 1
            continue
        bucket = m.bucket
        while True:
            total += 1
            if m.getkey() not in rulescount:
                rulescount[m.getkey()]    =    0
                m.minHopCount = m.hopCount
                m.maxHopCount = m.hopCount
                mapMcgList[m.getkey()] = mcgListIndex
                if not options.advanced :
                    mcglist    +=    [m]
                    mcgListIndex += 1
            if options.advanced :
                mcglist     +=      [m]
                mcgListIndex += 1
                m.minHopCount = m.hopCount
                m.maxHopCount = m.hopCount
            else :
                if mcglist[mapMcgList[m.getkey()]].minHopCount > m.hopCount :
                    mcglist[mapMcgList[m.getkey()]].minHopCount = m.hopCount
                if mcglist[mapMcgList[m.getkey()]].maxHopCount < m.hopCount :
                    mcglist[mapMcgList[m.getkey()]].maxHopCount = m.hopCount
            rulescount[m.getkey()] += 1
            next = m.next
            if next >= MCG_TABLE_SIZE:
                break
            try:
                if next not in mcgtable.keys():
                    output = m.read_wqdump_single_mcg(next)
                    fill_mcgtable(output, m.hopCount + 1, m.bucket)
                #m.bucket = bucket
                if  next ==  m.index :
                    break
                m = mcgtable[next]
            except Exception as e:
                sys.stderr.write('')
                sys.stderr.write('mcg	[0x%x].next	points	to	non	existing	mcg	index	0x%x\n'	%	(m.index,	next))
                break
            m.bucket = bucket
            progress('L')
        idx += 1
    
    if not options.quiet:
        sys.stderr.write('\n')
    
    m = Mcg()
    print ("%-6s %-6s  %s" % ('Bucket', 'Index', m.get_hdr_str()))
    
    dups = []
    uniqueRules = len(mcglist)
    qpsTable = {}
    for i, m in enumerate(mcglist):
        string = ""
        if len(m.qps) < 2:
            for qp in m.qps:
                string +=   "%x " % qp
        else :
            string = "SB"
            qpsTable[m.index] = m.qps
    
        rulecount = rulescount[m.getkey()]
        if rulecount == 1 or options.advanced:
            if options.advanced or (options.hopcount and m.member_cnt):
                print ("%6x %6x  %s   %d  %6x  %5s" % (int(m.bucket), int(m.index), m, m.hopCount, int(m.next), string))
            elif m.member_cnt :
                print ("%6x %6x  %s %6x  %s" % (int(m.bucket), int(m.index), m, int(m.next), string))
            else :
                total -= rulecount
                uniqueRules -= 1
        else:
            if options.hopcount and m.member_cnt:
                dups.append( "%6x %6x  %s   %d-%d %6x %5s  %5d" % (int(m.bucket), int(m.index), m, m.minHopCount, m.maxHopCount, int(m.next), string, rulecount))
            elif m.member_cnt :
                dups.append("%6x %6x  %s %6x %5s  %5d" % (int(m.bucket), int(m.index), m, int(m.next), string, rulecount))
            else :
                total -= rulecount
                uniqueRules -= 1
                
    if len(dups):
        spaces = "                                                                                                                                                                   "
        print ("Duplicated MCGS:" + spaces[0:len(dups[0])-20]+ "Count")
        for dup in dups :
            print (dup)
    
    if not options.advanced :
        print ("%d Unique rules, %d Total" % (uniqueRules, total))
    else :
        print ("%d Total" % total)
    
    
    
    if not len(qpsTable):
        sys.exit(0)
    
    print ("\n\nIndex          QPs")
    print ("=" * 117)
    start = "\n            "
    for (index, allQps) in qpsTable.iteritems():
        qps = ""
        allQps.sort()
        for i, qp in enumerate(allQps):
            if i % 15 == 0 and i != 0:
                qps += start
            qps += ("%6x " % qp)
        print ("%6x      %s" % (index, qps))
        print ("=" * 117)

except SystemExit as e:
    sys.exit(e)
except(KeyboardInterrupt, SystemExit):
    signalHandler()
except Exception as e:
    print ("-E- Oops UnExpected Error, Please report this to tool Owner !")
    raise e
    sys.exit(1)