From e4f733b420987e7f828dbe757f45de20c2dcff40 Mon Sep 17 00:00:00 2001
From: Steven Cahail <scahail@codeaurora.org>
Date: Wed, 28 Oct 2015 16:21:09 -0600
Subject: [PATCH] ipc_logging: Refactor cmdParse to reduce cyclomatic
 complexity

The cmdParse() function has excessive cyclomatic complexity.

Reduce the cyclomatic complexity by writing helper functions to obtain
version info, handle processing log contexts, and handle processing log
pages.

Change-Id: I15b8da9b20bb7b20daad3f0f0ac460b6cfe8ad89
---
 .../parsers/ipc_logging.py                    | 247 ++++++++++++------
 1 file changed, 168 insertions(+), 79 deletions(-)

diff --git a/linux-ramdump-parser-v2/parsers/ipc_logging.py b/linux-ramdump-parser-v2/parsers/ipc_logging.py
index d9c7b73..b94345a 100644
--- a/linux-ramdump-parser-v2/parsers/ipc_logging.py
+++ b/linux-ramdump-parser-v2/parsers/ipc_logging.py
@@ -1596,6 +1596,146 @@ def get_LogPage_v2_instance():
     return LogPage_v2()
 
 
+def get_page_of_version(data, versionIsOneOrGreater, version, page):
+    """
+    Retrieves the appropriate descendant of :class:`LogPage` given the version.
+    Note: It may seem unnecessary to pass the version itself and the
+    versionIsOneOrGreater boolean. This is done because of the way the version
+    is stored in the dumps. The version is stored in the log context, and
+    contexts are only present in dumps with IPC Logging version 1 or greater.
+    We can tell whether a page is version 1 or greater by looking just at the
+    page iteslf, but beyond that we must find the context. This is why both
+    variables are used.
+
+    :param data: The data that has been read in from the input file
+    :param versionIsOneOrGreater: True if IPC Logging version is one or
+                                  greater
+    :param version: The IPC Logging version
+    :param page: The log page currently being processed
+    """
+    dictVersions = {1: get_LogPage_v1_instance, 2: get_LogPage_v2_instance}
+
+    if page.isVersionOneOrGreater(data):
+        versionIsOneOrGreater = True
+        if version == 0:
+            """
+            This occurs before the context has been found; thus the version
+            has not been extracted and is not known. The version will be
+            extracted from the context later, and this function will be re-run
+            with the actual version. For now, use a LogPage_v1() object to
+            extract the context.
+            """
+            page = LogPage_v1()
+            return page, versionIsOneOrGreater
+
+        page = dictVersions[version]()
+    else:
+        versionIsOneOrGreater = False
+        page = LogPage_v0()
+
+    return page, versionIsOneOrGreater
+
+
+def process_log_context(context, page, start_of_page, fIn, dictContexts,
+                        lstFiles, fileCount, version):
+    """
+    Finds and processes the log context, and extracts the version information
+    from it. This function determines which descendant of :class:`LogPage`
+    should be used.
+
+    :param context: The LogContext object, which will either be populated or
+                    have the version information extracted from it.
+    :param page: A descendant of :class:`LogPage`, which will have a context
+                 linked to it if one is found.
+    :param start_of_page: The position in the file corresponding to the start
+                          of the log page.
+    :param fIn: A pointer to the input file.
+    :param dictContexts: A dictionary that maintains the
+                         log ID-to-:class:`LogContext` mapping.
+    :param lstFiles: A list of dictionaries containing the name, starting
+                     position, and ending position of each input file.
+    :param fileCount: An index into lstFiles, which indicates the current file
+                      being processed.
+    :param version: The LogPage version.
+
+    :return: A tuple with of four elements: A boolean indicating whether or not
+             to continue the current iteration of processing, the LogContext
+             object, the LogPage object, and the version.
+    """
+    if page.log_id not in dictContexts:
+        try:
+            context = get_context(start_of_page, fIn, page,
+                                  dictContexts, lstFiles,
+                                  fileCount)
+        except:
+            msg = "Context not found - skipping page " + \
+                  "and trying to continue with " + \
+                  "unknown log page version"
+            logging.debug(msg)
+            page = LogPageVersionUnknown()
+            return False, context, page, version
+
+        # Got the context, extract the version and start
+        # again
+        if version == 0:
+            version = context.version
+            fIn.seek(start_of_page)
+            page = LogPageVersionUnknown()
+            return False, context, page, version
+
+        return True, context, page, version
+    else:
+        context = dictContexts[page.log_id]
+        page.setContext(context)
+        return True, context, page, version
+
+
+def process_log_pages(dictLogs, page, versionIsOneOrGreater, fIn, fileName,
+                      data, context, version, options):
+    """
+    Finds and processes, and stores log pages.
+
+    :param dictLogs: The dictionary of logs to be populated by this function
+    :param page: The log page currently being processed
+    :param versionIsOneOrGreater: True if IPC Logging version is one or
+                                  greater
+    :param fIn: Pointer to the input file
+    :param data: The data that has been read in from the input file
+    :param context: The context of this log page
+    :param version: The IPC Logging version
+    """
+    if page.log_id not in dictLogs:
+        dictLogs[page.log_id] = {}
+
+    if page.page_num in dictLogs[page.log_id]:
+        logging.error("Duplicate page found log id 0x%x," +
+                      "page %d", page.log_id, page.page_num)
+
+    dictLogs[page.log_id][page.page_num] = page
+
+    if versionIsOneOrGreater:
+        logging.info(
+            "Found log page - context: %s, id: 0x%x, " +
+            "page: %d", page.context.name,
+            page.log_id, page.page_num)
+        page.log_info()
+        sys.stdout.flush()
+    else:
+        logging.info("Found log page: id 0x%x, page %d" %
+                     (page.log_id, page.page_num))
+        page.log_info()
+        sys.stdout.flush()
+
+    # for debug mode, save the extracted log pages
+    if options.debug_enabled:
+        if version >= 1:
+            page.debug_save_log_pages(fIn, fileName, data,
+                                      context.name, options)
+        else:
+            page.debug_save_log_pages(fIn, fileName, data,
+                                      options)
+
+
 def cmdParse(options):
     """
     The top-level function, which is called from the main entry point to the
@@ -1616,7 +1756,6 @@ def cmdParse(options):
     """
     dictLogs = {}
     dictContexts = {}
-    dictVersions = {2: get_LogPage_v2_instance}
     fileCount = 0
     version = 0
     versionIsOneOrGreater = False
@@ -1644,84 +1783,34 @@ def cmdParse(options):
             if page.unpack(data):
                 data += fIn.read(PAGE_SIZE-16)
 
-                if page.isVersionOneOrGreater(data):
-                    versionIsOneOrGreater = True
-                    page = LogPage_v1()
-                else:
-                    versionIsOneOrGreater = False
-                    page = LogPage_v0()
-
-                if version > 1:
-                    page = dictVersions[version]()
-
-                if page.unpack(data):
-                    if versionIsOneOrGreater:
-                        if page.log_id not in dictContexts:
-                            try:
-                                context = get_context(start_of_page, fIn, page,
-                                                      dictContexts, lstFiles,
-                                                      fileCount)
-                            except:
-                                msg = "Context not found - skipping page " + \
-                                      "and trying to continue with " + \
-                                      "unknown log page version"
-                                logging.debug(msg)
-                                page = LogPageVersionUnknown()
-                                continue
-
-                            # Got the context, extract the version and start
-                            # again
-                            if version == 0:
-                                version = context.version
-                                fIn.seek(start_of_page)
-                                page = LogPageVersionUnknown()
-                                continue
-
-                        else:
-                            context = dictContexts[page.log_id]
-                            page.setContext(context)
-
-                        if context is None:
-                            logging.debug("Context at 0x%x had no data, " +
-                                          "skipping this page",
-                                          page.context_offset)
-                            if options.debug_enabled:
-                                page.log_info()
-                            page = LogPageVersionUnknown()
-                            continue
-
-                    if page.log_id not in dictLogs:
-                        dictLogs[page.log_id] = {}
-
-                    if page.page_num in dictLogs[page.log_id]:
-                        logging.error("Duplicate page found log id 0x%x," +
-                                      "page %d", page.log_id, page.page_num)
-
-                    dictLogs[page.log_id][page.page_num] = page
-
-                    if versionIsOneOrGreater:
-                        logging.info(
-                            "Found log page - context: %s, id: 0x%x, " +
-                            "page: %d", page.context.name,
-                            page.log_id, page.page_num)
-                        page.log_info()
-                        sys.stdout.flush()
-                    else:
-                        logging.info("Found log page: id 0x%x, page %d" %
-                                     (page.log_id, page.page_num))
-                        page.log_info()
-                        sys.stdout.flush()
-
-                    # for debug mode, save the extracted log pages
-                    if options.debug_enabled:
-                        if version >= 1:
-                            page.debug_save_log_pages(fIn, fileName, data,
-                                                      context.name, options)
-                        else:
-                            page.debug_save_log_pages(fIn, fileName, data,
-                                                      options)
-
-                    page = LogPageVersionUnknown()
+                page, versionIsOneOrGreater = \
+                    get_page_of_version(data, versionIsOneOrGreater,
+                                        version, page)
+
+                if page.unpack(data) and versionIsOneOrGreater:
+                    continue_iter, context, page, version = \
+                        process_log_context(context, page, start_of_page, fIn,
+                                            dictContexts, lstFiles, fileCount,
+                                            version)
+
+                    if not continue_iter:
+                        continue
+
+                    if context is None:
+                        logging.debug("Context at 0x%x had no data, " +
+                                      "skipping this page",
+                                      page.context_offset)
+
+                        if options.debug_enabled:
+                            page.log_info()
+
+                        page = LogPageVersionUnknown()
+                        continue
+
+                process_log_pages(dictLogs, page, versionIsOneOrGreater,
+                                  fIn, fileName, data, context, version,
+                                  options)
+                page = LogPageVersionUnknown()
 
         fIn.close()
         fileCount += 1
-- 
GitLab