# Buggy code
def _CheckProcessStatus(self, pid):
    """Check status of a specific monitored process and take action on it.

    This method examines the status of a process, and then terminates it,
    signals other processes to terminate or starts a replacement process as
    appropriate.

    Args:
      pid: The process ID (PID).

    Raises:
      EngineAbort: when the collector or storage worker process
                   unexpectedly terminated.
      KeyError: if the process is not registered with the engine.
    """
    # TODO: Refactor this method, simplify and separate concerns (monitoring
    # vs management).
    self._RaiseIfNotRegistered(pid)

    process = self._processes_per_pid[pid]

    process_status = self._GetProcessStatus(process)
    if process_status is None:
        process_is_alive = False
    else:
        process_is_alive = True

    if isinstance(process_status, dict):
        self._rpc_errors_per_pid[pid] = 0
        status_indicator = process_status.get("processing_status", None)

    else:
        rpc_errors = self._rpc_errors_per_pid.get(pid, 0) + 1
        self._rpc_errors_per_pid[pid] = rpc_errors

        if rpc_errors > self._MAXIMUM_RPC_ERRORS:
            process_is_alive = False

        if process_is_alive:
            rpc_port = process.rpc_port.value
            logging.warning(
                (
                    "Unable to retrieve process: {0:s} (PID: {1:d}) status via "
                    "RPC socket: http://localhost:{2:d}"
                ).format(process.name, pid, rpc_port)
            )

            processing_status_string = "RPC error"
            status_indicator = definitions.PROCESSING_STATUS_RUNNING
        else:
            processing_status_string = "killed"
            status_indicator = definitions.PROCESSING_STATUS_KILLED

        process_status = {
            "processing_status": processing_status_string,
            "type": process.type,
        }

    if status_indicator == definitions.PROCESSING_STATUS_ERROR:
        path_spec = process_status.get("path_spec", None)
        if path_spec:
            self._processing_status.error_path_specs.append(path_spec)

    self._UpdateProcessingStatus(pid, process_status)

    if status_indicator not in [
        definitions.PROCESSING_STATUS_COMPLETED,
        definitions.PROCESSING_STATUS_INITIALIZED,
        definitions.PROCESSING_STATUS_RUNNING,
    ]:
        logging.error(
            (
                "Process {0:s} (PID: {1:d}) is not functioning correctly. "
                "Status code {2!s}."
            ).format(process.name, pid, status_indicator)
        )

        self._processing_status.error_detected = True

        if process.type == definitions.PROCESS_TYPE_COLLECTOR:
            raise errors.EngineAbort("Collector unexpectedly terminated")

        if process.type == definitions.PROCESS_TYPE_STORAGE_WRITER:
            raise errors.EngineAbort("Storage writer unexpectedly terminated")

        self._TerminateProcess(pid)

        logging.info(
            "Starting replacement worker process for {0:s}".format(process.name)
        )
        worker_process = self._StartExtractionWorkerProcess()
        self._StartMonitoringProcess(worker_process.pid)

    elif status_indicator == definitions.PROCESSING_STATUS_COMPLETED:
        if process.type == definitions.PROCESS_TYPE_WORKER:
            number_of_events = process_status.get("number_of_events", 0)
            number_of_pathspecs = process_status.get("consumed_number_of_path_specs", 0)
            logging.debug(
                (
                    "Process {0:s} (PID: {1:d}) has completed its processing. "
                    "Total of {2:d} events extracted from {3:d} pathspecs"
                ).format(process.name, pid, number_of_events, number_of_pathspecs)
            )
        elif process.type == definitions.PROCESS_TYPE_COLLECTOR:
            number_of_pathspecs = process_status.get("produced_number_of_path_specs", 0)
            logging.debug(
                (
                    "Process {0:s} (PID: {1:d}) has completed its processing. "
                    "Total of {2:d} pathspecs extracted"
                ).format(process.name, pid, number_of_pathspecs)
            )
        elif process.type == definitions.PROCESS_TYPE_STORAGE_WRITER:
            logging.debug(
                (
                    "Process {0:s} (PID: {1:d}) has completed its processing. "
                    "Total of {2:d} events written"
                ).format(process.name, pid, process_status.get("number_of_events", 0))
            )

        self._StopMonitoringProcess(pid)

    elif self._show_memory_usage:
        self._LogMemoryUsage(pid)


# Traceback (CError)
plaso-1.3.1.post20151021

^CError in atexit._run_exitfuncs:
Traceback (most recent call last):
File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
func(*targs, **kargs)
File "/usr/lib/python2.7/multiprocessing/util.py", line 325, in _exit_function
p.join()
File "/usr/lib/python2.7/multiprocessing/process.py", line 145, in join
res = self._popen.wait(timeout)
File "/usr/lib/python2.7/multiprocessing/forking.py", line 154, in wait
return self.poll(0)
File "/usr/lib/python2.7/multiprocessing/forking.py", line 135, in poll
pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt
Error in sys.exitfunc:
Traceback (most recent call last):
File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
func(*targs, **kargs)
File "/usr/lib/python2.7/multiprocessing/util.py", line 325, in _exit_function
p.join()
File "/usr/lib/python2.7/multiprocessing/process.py", line 145, in join
res = self._popen.wait(timeout)
File "/usr/lib/python2.7/multiprocessing/forking.py", line 154, in wait
return self.poll(0)
File "/usr/lib/python2.7/multiprocessing/forking.py", line 135, in poll
pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored
Exception TypeError: "'NoneType' object is not callable" in <Finalize object, dead> ignored\


analyst   4035  3335  0 Oct29 pts/19   00:00:57 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4045  4035  0 Oct29 pts/19   00:01:02 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4048  4035  0 Oct29 pts/19   00:00:39 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4051  4035  0 Oct29 pts/19   00:00:58 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4054  4035  0 Oct29 pts/19   00:00:41 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4060  4035  0 Oct29 pts/19   00:00:44 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4063  4035  0 Oct29 pts/19   00:00:40 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4066  4035  0 Oct29 pts/19   00:00:37 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4068  4035  0 Oct29 pts/19   00:00:40 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4070  4035  0 Oct29 pts/19   00:11:09 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4073  4035  0 Oct29 pts/19   00:00:37 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4076  4035  0 Oct29 pts/19   00:00:37 /usr/bin/python /usr/local/bin/log2timeline.py -p --vss-stores 1 srt.dump /media/usb1
analyst   4083  4035  1 Oct29 pts/19   00:14:40 [log2timeline.py] <defunct>

# Fixed code
def _CheckProcessStatus(self, pid):
    """Check status of a specific monitored process and take action on it.

    This method examines the status of a process, and then terminates it,
    signals other processes to terminate or starts a replacement process as
    appropriate.

    Args:
      pid: The process ID (PID).

    Raises:
      EngineAbort: when the collector or storage worker process
                   unexpectedly terminated.
      KeyError: if the process is not registered with the engine.
    """
    # TODO: Refactor this method, simplify and separate concerns (monitoring
    # vs management).
    self._RaiseIfNotRegistered(pid)

    process = self._processes_per_pid[pid]

    process_status = self._GetProcessStatus(process)
    if process_status is None:
        process_is_alive = False
    else:
        process_is_alive = True

    if isinstance(process_status, dict):
        self._rpc_errors_per_pid[pid] = 0
        status_indicator = process_status.get("processing_status", None)

    else:
        rpc_errors = self._rpc_errors_per_pid.get(pid, 0) + 1
        self._rpc_errors_per_pid[pid] = rpc_errors

        if rpc_errors > self._MAXIMUM_RPC_ERRORS:
            process_is_alive = False

        if process_is_alive:
            rpc_port = process.rpc_port.value
            logging.warning(
                (
                    "Unable to retrieve process: {0:s} (PID: {1:d}) status via "
                    "RPC socket: http://localhost:{2:d}"
                ).format(process.name, pid, rpc_port)
            )

            processing_status_string = "RPC error"
            status_indicator = definitions.PROCESSING_STATUS_RUNNING
        else:
            processing_status_string = "killed"
            status_indicator = definitions.PROCESSING_STATUS_KILLED

        process_status = {
            "processing_status": processing_status_string,
            "type": process.type,
        }

    if status_indicator == definitions.PROCESSING_STATUS_ERROR:
        path_spec = process_status.get("path_spec", None)
        if path_spec:
            self._processing_status.error_path_specs.append(path_spec)

    self._UpdateProcessingStatus(pid, process_status)

    if status_indicator not in [
        definitions.PROCESSING_STATUS_COMPLETED,
        definitions.PROCESSING_STATUS_INITIALIZED,
        definitions.PROCESSING_STATUS_RUNNING,
        definitions.PROCESSING_STATUS_PARSING,
        definitions.PROCESSING_STATUS_HASHING,
    ]:
        logging.error(
            (
                "Process {0:s} (PID: {1:d}) is not functioning correctly. "
                "Status code {2!s}."
            ).format(process.name, pid, status_indicator)
        )

        self._processing_status.error_detected = True

        if process.type == definitions.PROCESS_TYPE_COLLECTOR:
            raise errors.EngineAbort("Collector unexpectedly terminated")

        if process.type == definitions.PROCESS_TYPE_STORAGE_WRITER:
            raise errors.EngineAbort("Storage writer unexpectedly terminated")

        self._TerminateProcess(pid)

        logging.info(
            "Starting replacement worker process for {0:s}".format(process.name)
        )
        worker_process = self._StartExtractionWorkerProcess()
        self._StartMonitoringProcess(worker_process.pid)

    elif status_indicator == definitions.PROCESSING_STATUS_COMPLETED:
        if process.type == definitions.PROCESS_TYPE_WORKER:
            number_of_events = process_status.get("number_of_events", 0)
            number_of_pathspecs = process_status.get("consumed_number_of_path_specs", 0)
            logging.debug(
                (
                    "Process {0:s} (PID: {1:d}) has completed its processing. "
                    "Total of {2:d} events extracted from {3:d} pathspecs"
                ).format(process.name, pid, number_of_events, number_of_pathspecs)
            )
        elif process.type == definitions.PROCESS_TYPE_COLLECTOR:
            number_of_pathspecs = process_status.get("produced_number_of_path_specs", 0)
            logging.debug(
                (
                    "Process {0:s} (PID: {1:d}) has completed its processing. "
                    "Total of {2:d} pathspecs extracted"
                ).format(process.name, pid, number_of_pathspecs)
            )
        elif process.type == definitions.PROCESS_TYPE_STORAGE_WRITER:
            logging.debug(
                (
                    "Process {0:s} (PID: {1:d}) has completed its processing. "
                    "Total of {2:d} events written"
                ).format(process.name, pid, process_status.get("number_of_events", 0))
            )

        self._StopMonitoringProcess(pid)

    elif self._show_memory_usage:
        self._LogMemoryUsage(pid)