mobileInsight-core Part-II: Analyzer

In this series, we will review the codes in mobileInsight-desktop, the core modules and desktop version of MobileInsight. Following the discussions in Part-I, this article introduces analyzers, the core modules in MobileInsight for cellular protocol analytics. Besides, we will also introduce other misc modules in mobileInsight-desktop.

Analyzers

Overview

Given cellular messages from Monitors, MobileInsight further triggers analyzers to perform diverse cellular analytics. We have realized various analyzers, all of which are included in mobile_insight/analyzer, as shown below (this list still goes on and long):

./mobileInsight-desktop/mobile_insight/analyzer/
├── __init__.py
├── analyzer.py
├── wcdma_rrc_analyzer.py
├── log_analyzer.py
├── lte_mac_analyzer.py
├── lte_rrc_analyzer.py
├── lte_nas_analyzer.py
├── lte_pdcp_analyzer.py
├── lte_phy_analyzer.py
├── lte_rlc_analyzer.py
└── ...

Some common analyzers are summarized as below. Note that to develop a cellular protocol analyzer, it is usually required to have a comprehensive understanding of the 3G/4G cellular protocols. This requires to read 3GPP standards, as listed below. Instead of elaborating the protocol-specific analytics, this article will focus on the common APIs, conventions, and structures of an analyzer. We next elaborate it.

Analyze r Descrip tion Standar ds/Refere nce
lte_mac_analyzer .py 4G MAC protocol analyzer TS36.321
lte_meas urement_ analyzer. py Reports 4G-RRC measureme nt results TS36.331
lte_nas_analyzer .py 4G EMM/ESM protocol analyzer TS24.301
lte_pdcp _analyze r.py 4G PDCP protocol analyzer TS36.323
lte_phy_analyzer .py 4G PHY layer (LL1) analyzer TS36.213, TS36.211
lte_rlc_analyzer .py 4G RLC protocol analyzer TS36.322
lte_rrc_analyzer .py 4G RRC protocol analyzer TS36.331
umts_nas _analyze r.py 3G MM/GMM/SM /CM protocol analyzer TS24.008
mobility_mngt.py 4G handoff decision logic inference TS36.304, TS36.331, MobileIns ight paper [Mobicom’ 16]
wcdma_rr c_analyz er.py 3G-RRC protocol analyzer TS25.331
modem_de bug_anal yzer.py analyzer for Qualcom’s modem debug messages N/A
msg_logg er.py A simple message dumper to stdio and/or file N/A
protocol_analyzer .py An abstracti on of tracking protocol state machine N/A

Base class: Analyzer

All MobileInsight analyzers should inherit from the base class Analyzers, which defines the basic interface for a monitor: - Declare its dependency on cellular message types: enable_log; - Declare its dependency on other analyzers: include_analyzer; - Bind to a monitor: set_source; - Callbacks in response to events from monitor (add_source_callback) or other analyzers (include_analyzer); - (Optional) pushes runtime cellular information to other mobile apps (with MI-APP API).

Each analyzer should overload these interfaces based on its specific environment. The following code snippet summaries the abstract interfaces (complete code in mobile_insight/monitor/analyzer.py):

from ..element import Element, Event

class Analyzer(Element):

    def __init__(self):
        Element.__init__(self)

        """
        Task lists
        1. Initialize empty monitor
        2. Initialize empty list of callbacks for monitor and other analyzers
        """
        # ...

    def set_source(self,source):
        """
        Bind this analyzer to a monitor (source)
        The messages from the source will drive the analysis.
        All parent analyzers this analyzer depends on will also be recursively bound to source
        """

    def add_source_callback(self,callback):
        """
        Add a callback function to the analyzer.
        When a message arrives, the analyzer will trigger the callbacks for analysis.
        """

    def rm_source_callback(self,callback):
        """
        Delete a callback function to the analyzer.
        """

    def include_analyzer(self,analyzer_name,callback_list,*args):
        """
        Declares the dependency from other analyzers.
        Once declared, the current analyzer will receive events
        from other analyzers, then trigger functions in callback_list
        """

    def recv(self,module,event):
        """
        Handle the received events.
        This is an overload member from Element
        """

In the following, we will use one example LtePhyAnalyzer to show how to develop your own analyzer, and overload above interfaces.

Declare its dependency on cellular message types: enable_log

An analyzer should declare the cellular messages it need for protocol analytics. This is realized by calling enable_log, which is typically called when binding the analyzer to the monitor. In this way, the monitor will activate the corresponding message collection at hardware, and pushes this message to analyzer by calling the source callbacks (registered via add_source_callback).

The following shows one example of how to achieve it. In writing a new analyzer, we should overload set_source to include supported message types, and register the callback in __init__ initialization function.

def set_source(self,source):
        """
        Set the trace source. Enable the cellular signaling messages

        :param source: the trace source (collector).
        """
        Analyzer.set_source(self,source)

        #Phy-layer logs
        source.enable_log("LTE_PHY_PDSCH_Packet")
        source.enable_log("LTE_PHY_PUSCH_CSF")

def __init__(self):
        Analyzer.__init__(self)

        self.add_source_callback(self.__msg_callback)
        # ...

Declare analyzer dependency include_analyzer

In many cases, your analyzer can be built on top of existing analyzers, thus reusing their analytical results. This modular approach simplifies the development, and makes code reusing possible. Typically, to declare the dependency of an analyzer (i.e., “parent analyzer”), your analyzer should call include_analyzer during the initialization (in __init__), and passes a callback function in response to these analyzer’s events. The following code shows how it works:

def __init__(self):
        Analyzer.__init__(self)
        #...
        #include analyzers
        self.include_analyzer("LteRrcAnalyzer",[self.__on_event])
        self.include_analyzer("WcdmaRrcAnalyzer",[self.__on_event])
        #...

def __on_event(self,msg):
        """callbacks for the 3G/4G RRC messages"""
        #...

Event-driven Callbacks

For both monitor and parent analyzer, an analyzer should provide a callback function in response to their cellular events. These callbacks are registered when binding to a monitor (add_source_callback), or declaring a parent analyzer (include_analyzer). In both cases, the callback takes one parameter msg as input, which is the event raised by monitor or parent analyzer. Inside the callback, you can decode this message, and perform specific analytics tasks.

The following shows the cellular message callback in LtePhyAnalyzer. This callback is triggered by cellular messages from Monitor. It first determines the message type, decode the messages, then take specific actions.

def __msg_callback(self,msg):
    if msg.type_id == "LTE_PHY_PDSCH_Packet":
        self.callback_pdsch(msg)
    elif msg.type_id == "LTE_PHY_PUSCH_CSF":
        self.callback_pusch(msg)
    elif msg.type_id ==  "LTE_MAC_UL_Tx_Statistics":
        self.callback_pusch_grant(msg)

def callback_pusch(self,msg):
    """
    Callback for LTE_PHY_PUSCH_CSF.
    Currently it updates CQI.

    :param msg: raw LTE_PHY_PUSCH_CSF packet
    """

    log_item = msg.data.decode()
    self.cur_cqi0 = log_item['WideBand CQI CW0']
    self.cur_cqi1 = log_item['WideBand CQI CW1']

Push notifications: MI-APP

Besides event-driven callbacks, the analyzer also provides another interface to push analytics results to other mobile apps. This is called MI-APP API. To learn how to use it, you can read the following tutorial:

How to use “MI-APP API`? <mi-app>`__

Other Modules

Wireshark helper: ws_dissector

ws_dissector is simply a wrapper of Wireshark’s parsing functions. It is called by DmLogPacket (which calls WSDissector in `mobile_insight/monitor/dm_collector/dm_endec/ws_dissector.py) to parse cellular messages. The structure is as follows:

.
├── Makefile # Makefile
├── WiresharkCMakeTarget.txt #CMake configuration for Wireshark
├── packet-aww.cpp
├── packet-aww.h
└── ws_dissector.cpp # Wrapper of wireshark parsers

Desktop-version packaging and installation: setup.py and MANIFEST.in

MobileInsight leverages the standardized Python setup scripts to package its desktop-version modules. It has two files:

  • “MANIFEST.in“: it specifies which files to be included, which files should not be included (such as dm_collector_c). It follows the standardized Python setup script format, as shown below (mobileInsight-desktop/MANIFEST.in):
include README.md
include LICENSE
include setup.py
recursive-include GUI *
recursive-include examples *.json *.py *.mi2log *.mi2app *.txt
recursive-include mobile_insight *.py  *.m *.pyd README
recursive-exclude examples *.db
recursive-exclude mobile_insight *.db
exclude mobile_insight/monitor/android_qmdl_monitor.py
exclude mobile_insight/analyzer/handoff_loop_analyzer.py
prune dm_collector_c
prune docs/docs
  • “setup.py“: this file is used to install MobileInsight on desktop. It is a standardized Python setup script, and implements the following installation logic:
  1. It checks the desktop’s OS version (Win/OS X/Linux), and downloads the corresponding ws_dissector, ‘dm_collector_c.so/pyd` and wireshark libraries;
  2. It copies all Python modules to Python-2.7’s default directory, and other binaries/libraries to system folders.

Desktop-version GUI

The details of this GUI can be found here:

Using MobileInsight GUI