예제 #1
0
def fVersionCheck():
    import os, platform, sys
    oConsole.fLock()
    try:
        oConsole.fPrint("+ ", INFO, "Windows", NORMAL, " version: ", INFO, mWindowsAPI.oWindowsVersion.sProductName, \
          NORMAL, " release ", INFO, mWindowsAPI.oWindowsVersion.sReleaseId, NORMAL, ", build ", INFO, \
          mWindowsAPI.oWindowsVersion.sCurrentBuild, NORMAL, " ", INFO, mWindowsAPI.oWindowsVersion.sISA, NORMAL, ".")
        oConsole.fPrint("+ ", INFO, "Python", NORMAL, " version: ", INFO, str(platform.python_version()), NORMAL, " ", \
          INFO, mWindowsAPI.fsGetPythonISA(), NORMAL, ".")
        axModules = [
            ("BugId", "__main__", oVersionInformation),
            ("cBugId", "cBugId", cBugId.oVersionInformation),
            ("mFileSystem", "mFileSystem", mFileSystem.oVersionInformation),
            ("mWindowsAPI", "mWindowsAPI", mWindowsAPI.oVersionInformation),
            ("oConsole", "oConsole", oConsole.oVersionInformation),
        ]
        uCounter = 0
        for (sModuleName, sSysModuleName,
             oModuleVersionInformation) in axModules:
            assert sModuleName == oModuleVersionInformation.sProjectName, \
                "Module %s reports that it is called %s" % (sModuleName, oModuleVersionInformation.sProjectName)
            sInstallationPath = os.path.dirname(
                sys.modules[sSysModuleName].__file__)
            oConsole.fPrint("+ ", INFO, oModuleVersionInformation.sProjectName, NORMAL, " version: ", INFO, \
                oModuleVersionInformation.sCurrentVersion, NORMAL, ", installed at ", INFO, sInstallationPath, NORMAL, ".")
            oConsole.fProgressBar(uCounter * 1.0 / len(axModules), \
                "* Checking %s for updates..." % oModuleVersionInformation.sProjectName)
            if oModuleVersionInformation.bPreRelease:
                oConsole.fPrint("  You are running a ", HILITE, "pre-release",
                                NORMAL, " version: ",
                                "the latest release version is ", INFO,
                                oModuleVersionInformation.sLatestVersion,
                                NORMAL, ".")
            elif not oModuleVersionInformation.bUpToDate:
                oConsole.fPrint("  Version ", HILITE,
                                oModuleVersionInformation.sLatestVersion,
                                NORMAL, " is available at ", HILITE,
                                oModuleVersionInformation.sUpdateURL, NORMAL,
                                ".")
            uCounter += 1
        oConsole.fPrint()
    finally:
        oConsole.fUnlock()
예제 #2
0
  def __init__(oCdbWrapper,
    sCdbISA, # Which version of cdb should be used to debug this application? ("x86" or "x64")
    sApplicationBinaryPath,
    auApplicationProcessIds,
    sUWPApplicationPackageName,
    sUWPApplicationId,
    asApplicationArguments,
    asLocalSymbolPaths,
    asSymbolCachePaths, 
    asSymbolServerURLs,
    dsURLTemplate_by_srSourceFilePath,
    bGenerateReportHTML,        
    uProcessMaxMemoryUse,
    uTotalMaxMemoryUse,
    uMaximumNumberOfBugs,
  ):
    if sCdbISA:
      assert not (sCdbISA == "x64" and fsGetPythonISA() == "x86"), \
          "You cannot use a 64-bit version of cdb.exe when you are using a 32-bit version of Python.";
      oCdbWrapper.sCdbISA = sCdbISA;
    else:
      oCdbWrapper.sCdbISA = fsGetPythonISA();
    assert sApplicationBinaryPath or auApplicationProcessIds or sUWPApplicationPackageName, \
        "You must provide one of the following: an application command line, a list of process ids or an application package name";
    oCdbWrapper.sApplicationBinaryPath = sApplicationBinaryPath;
    oCdbWrapper.oUWPApplication = sUWPApplicationPackageName and cUWPApplication(sUWPApplicationPackageName, sUWPApplicationId) or None;
    oCdbWrapper.auApplicationProcessIds = auApplicationProcessIds or [];
    oCdbWrapper.auMainProcessIds = oCdbWrapper.auApplicationProcessIds[:];
    oCdbWrapper.bApplicationStarted = False;
    oCdbWrapper.bUWPApplicationStarted = False;
    oCdbWrapper.bStopping = False;
    oCdbWrapper.auProcessIdsPendingAttach = [];
    oCdbWrapper.auProcessIdsThatNeedToBeResumedAfterAttaching = [];
    oCdbWrapper.asApplicationArguments = asApplicationArguments;
    oCdbWrapper.asLocalSymbolPaths = asLocalSymbolPaths or [];
    oCdbWrapper.asSymbolCachePaths = asSymbolCachePaths;
    if asSymbolCachePaths is None:
      oCdbWrapper.asSymbolCachePaths = dxConfig["asDefaultSymbolCachePaths"];
    oCdbWrapper.asSymbolServerURLs = asSymbolServerURLs;
    if asSymbolServerURLs is None:
      oCdbWrapper.asSymbolServerURLs = dxConfig["asDefaultSymbolServerURLs"];
    oCdbWrapper.dsURLTemplate_by_srSourceFilePath = dsURLTemplate_by_srSourceFilePath or {};
    oCdbWrapper.bGenerateReportHTML = bGenerateReportHTML;
    oCdbWrapper.uProcessMaxMemoryUse = uProcessMaxMemoryUse;
    oCdbWrapper.uTotalMaxMemoryUse = uTotalMaxMemoryUse;
    oCdbWrapper.oEventCallbacksLock = threading.Lock();
    oCdbWrapper.dafEventCallbacks_by_sEventName = {
      # These are the names of all the events that cCdbWrapper can throw. If it's not in the list, you cannot use it in
      # `fAddEventCallback`, `fRemoveEventCallback`, or `fbFireEvent`. The same event names are used by cBugId, but
      # any
      "Application resumed": [], # ()
      "Application running": [], # ()
      "Application debug output": [], # (cProcess oProcess, str[] asOutput)
      "Application stderr output": [], # (mWindowsAPI.cConsoleProcess oConsoleProcess, str sOutput)
      "Application stdout output": [], # (mWindowsAPI.cConsoleProcess oConsoleProcess, str sOutput)
      "Application suspended": [], # (str sReason)
      "Bug report": [], # (cBugReport oBugReport)
      "Cdb stderr output": [], # (str sOutput)
      "Cdb stdin input": [], # (str sInput)
      "Cdb stdout output": [], # (str sOutput)
      "Failed to apply application memory limits": [], # (cProcess oProcess)
      "Failed to apply process memory limits": [], # (cProcess oProcess)
      "Failed to debug application": [], # (str sReason)
      "Finished": [], # ()
      "Internal exception": [], # (Exception oException, traceback oTraceBack)
      "Log message": [], # (str sDescription, dict dxData)
      "License errors": [], # (str[] asErrors)
      "License warnings": [], # (str[] asWarnings)
      "Page heap not enabled": [], # (cProcess oProcess, bool bPreventable)
      "Cdb ISA not ideal": [], # (cProcess oProcess, str sCdbISA, bool bPreventable)
      "Process attached": [], # (cProcess oProcess)
      "Process started": [], # (mWindowsAPI.cConsoleProcess oConsoleProcess)
      "Process terminated": [], #(cProcess oProcess)
    };

  
    # This is where we keep track of the threads that are executing (for debug purposes):
    oCdbWrapper.aoActiveHelperThreads = [];
    # Get the cdb binary path
    oCdbWrapper.sDebuggingToolsPath = dxConfig["sDebuggingToolsPath_%s" % oCdbWrapper.sCdbISA];
    assert oCdbWrapper.sDebuggingToolsPath, "No %s Debugging Tools for Windows path found" % oCdbWrapper.sCdbISA;
    oCdbWrapper.doProcess_by_uId = {};
    oCdbWrapper.doConsoleProcess_by_uId = {};
    oCdbWrapper.oCdbCurrentProcess = None; # The current process id in cdb's context
    oCdbWrapper.oCdbCurrentThread = None; # The current thread id in cdb's context
    oCdbWrapper.sCdbCurrentISA = None; # The ISA cdb is debugging the current process in (can differ from the process' ISA!)
    # Initialize some variables
    if bGenerateReportHTML:
      oCdbWrapper.sCdbIOHTML = ""; # Logs stdin/stdout/stderr for the cdb process, grouped by executed command.
      oCdbWrapper.sPromptHTML = None; # Logs cdb prompt to be adde to CdbIOHTML if a command is added.
      if dxConfig["bLogInReport"]:
        oCdbWrapper.sLogHTML = ""; # Logs various events that may be relevant
    oCdbWrapper.bCdbRunning = True; # Set to False after cdb terminated, used to terminate the debugger thread.
    oCdbWrapper.bCdbWasTerminatedOnPurpose = False; # Set to True when cdb is terminated on purpose, used to detect unexpected termination.
    # To make it easier to refer to cdb breakpoints by id, a mechanism must be used to allocate and release them
    # See fuGetBreakpointId and fReleaseBreakpointId for implementation details.
    oCdbWrapper.oBreakpointCounter = itertools.count(); # None have been used so far, so start at 0.
    # You can set a breakpoint that results in a bug being reported when it is hit.
    # See fuAddBugBreakpoint and fReleaseBreakpointId for implementation details.
    oCdbWrapper.duProcessId_by_uBreakpointId = {};
    oCdbWrapper.duAddress_by_uBreakpointId = {};
    oCdbWrapper.dfCallback_by_uBreakpointId = {};
    oCdbWrapper.dauOldBreakpointAddresses_by_uProcessId = {};
    # You can tell BugId to check for excessive CPU usage among all the threads running in the application.
    # See fSetCheckForExcessiveCPUUsageTimeout and cExcessiveCPUUsageDetector.py for more information
    oCdbWrapper.oExcessiveCPUUsageDetector = cExcessiveCPUUsageDetector(oCdbWrapper);
    # Keep track of timeouts that should fire at some point in the future and timeouts that should fire now.
    oCdbWrapper.aoTimeouts = [];
    oCdbWrapper.bApplicationIsRunnning = False; # Will be set to true while the application is running in cdb.
    oCdbWrapper.uUtilityInterruptThreadId = None; # Will be set to the thread id in which we triggered an AV to
                                                  # interrupt the application.
    # Lock for the above four timeout and interrupt variables
    oCdbWrapper.oTimeoutAndInterruptLock = threading.RLock();
    # Keep track of how long the application has been running, used for timeouts (see foSetTimeout, fCdbStdInOutThread
    # and fCdbInterruptOnTimeoutThread for details. The debugger can tell is what time it thinks it is before we start
    # and resume the application as well as what time it thinks it was when an exception happened. The difference is
    # used to calculate how long the application has been running. We cannot simply use time.clock() before start/
    # resuming and at the time an event is handled as the debugger may take quite some time processing an event before
    # we can call time.clock(): this time would incorrectly be added to the time the application has spent running.
    # However, while the application is running, we cannot ask the debugger what time it thinks it is, so we have to 
    # rely on time.clock(). Hence, both values are tracked.
    oCdbWrapper.oApplicationTimeLock = threading.RLock();
    oCdbWrapper.nConfirmedApplicationRunTime = 0; # Total time spent running before last interruption
    oCdbWrapper.nApplicationResumeDebuggerTime = None;  # debugger time at the moment the application was last resumed
    oCdbWrapper.nApplicationResumeTime = None;          # time.clock() at the moment the application was last resumed
    oCdbWrapper.oCdbConsoleProcess = None;
    
    oCdbWrapper.oCollateralBugHandler = cCollateralBugHandler(oCdbWrapper, uMaximumNumberOfBugs);
    
    # Create VERIFIER STOP and Asan ERROR detectors. The later also needs to be called when a Breakpoint exception
    # happens in order to report it (the former reports the error as soon as it is detected in applicationm debug
    # output).
    cVerifierStopDetector(oCdbWrapper);
    oCdbWrapper.oASanErrorDetector = cASanErrorDetector(oCdbWrapper);
    
    if oCdbWrapper.bGenerateReportHTML and dxConfig["bLogInReport"]:
      def fWriteLogMessageToReport(sMessage, dsData = None):
        sData = dsData and ", ".join(["%s: %s" % (sName, sValue) for (sName, sValue) in dsData.items()]);
        oCdbWrapper.sLogHTML += "<span class=\"LogMessage\">%s%s</span><br/>\n" % \
            (oCdbWrapper.fsHTMLEncode(sMessage), sData and " (%s)" % oCdbWrapper.fsHTMLEncode(sData) or "");
      oCdbWrapper.fAddEventCallback("Log message", fWriteLogMessageToReport);
예제 #3
0
def fPrintVersionInformation(bCheckForUpdates, bCheckAndShowLicenses, bShowInstallationFolders):
  # Read product details for rs and all modules it uses.
  aoProductDetails = mProductDetails.faoGetProductDetailsForAllLoadedModules();
  oMainProductDetails = mProductDetails.foGetProductDetailsForMainModule();
  if bCheckForUpdates:
    uCheckedProductCounter = 0;
    for oProductDetails in aoProductDetails:
      oConsole.fProgressBar(
        uCheckedProductCounter * 1.0 / len(aoProductDetails),
        "Checking %s for updates..." % oProductDetails.sProductName,
      );
      try:
        oProductDetails.oLatestProductDetailsFromRepository;
      except Exception as oException:
        oConsole.fPrint(
          ERROR, u"- Version check for ", ERROR_INFO, oProductDetails.sProductName,
          ERROR, " failed: ", ERROR_INFO, str(oException),
        );
      uCheckedProductCounter += 1;
  oConsole.fLock();
  try:
    if bCheckAndShowLicenses:
      aoLicenses = [];
      asLicensedProductNames = [];
      asProductNamesInTrial = [];
      asUnlicensedProductNames = [];
      for oProductDetails in aoProductDetails:
        if oProductDetails.oLicense:
          if oProductDetails.oLicense not in aoLicenses:
            aoLicenses.append(oProductDetails.oLicense);
          asLicensedProductNames.append(oProductDetails.sProductName);
        elif oProductDetails.bHasTrialPeriod and oProductDetails.bInTrialPeriod:
          asProductNamesInTrial.append(oProductDetails.sProductName);
        else:
          asUnlicensedProductNames.append(oProductDetails.sProductName);

      oLicenseCheckServer = mProductDetails.cLicenseCheckServer(oMainProductDetails.sLicenseServerURL);
      uCheckedLicenseCounter = 0;
      for oLicense in aoLicenses:
        oConsole.fProgressBar(
          uCheckedLicenseCounter * 1.0 / len(aoLicenses),
          "Checking license %s with server..." % oLicense.sLicenseId,
        );
        sLicenseCheckServerError = oLicense.fsCheckWithServerAndGetError(oLicenseCheckServer, bForceCheck = True);
        if sLicenseCheckServerError:
          oConsole.fPrint(
            ERROR, u"- License check for ", ERROR_INFO, oLicense.sLicenseId,
            ERROR, " on server ", ERROR_INFO, oMainProductDetails.sLicenseServerURL,
            ERROR, " failed: ", ERROR_INFO, sLicenseCheckServerError,
          );
        uCheckedLicenseCounter += 1;
    
    oConsole.fPrint(
      u"\u250C\u2500 ", INFO, "Version information", NORMAL, " ", sPadding = u"\u2500"
    );
    # Output the BugId product information first, then its dependencies:
    fPrintProductDetails(oMainProductDetails, bIsMainProduct = True, bShowInstallationFolders = bShowInstallationFolders);
    for oProductDetails in aoProductDetails:
      if oProductDetails != oMainProductDetails:
        fPrintProductDetails(oProductDetails, bIsMainProduct = False, bShowInstallationFolders = bShowInstallationFolders);
    
    oConsole.fPrint(
      u"\u2502 \u2219 ", INFO, "Windows",
      NORMAL, " version: ", INFO, oSystemInfo.sOSName,
      NORMAL, " release ", INFO, oSystemInfo.sOSReleaseId,
      NORMAL, ", build ", INFO, oSystemInfo.sOSBuild,
      NORMAL, " ", INFO, oSystemInfo.sOSISA,
      NORMAL, ".",
    );
    oConsole.fPrint(
      u"\u2502 \u2219 ", INFO, "Python",
      NORMAL, " version: ", INFO, str(platform.python_version()),
      NORMAL, " ", INFO, fsGetPythonISA(),
      NORMAL, ".",
    );
    
    if bCheckAndShowLicenses:
      oConsole.fPrint(
        u"\u251C\u2500 ", INFO, "License information", NORMAL, " ", sPadding = u"\u2500",
      );
      if aoLicenses:
        oConsole.fPrint(
          NORMAL, u"\u2502 \u2219 This system is registered with id ", INFO, mProductDetails.fsGetSystemId(), NORMAL, " on the license server",
        );
      for oLicense in aoLicenses:
        oConsole.fPrint(
          u"\u2502 \u2219 License ", INFO, oLicense.sLicenseId,
          NORMAL, " for ", INFO, oLicense.asProductNames[0], 
          NORMAL, " covers ", INFO, oLicense.sUsageTypeDescription, 
          NORMAL, " by ", INFO, oLicense.sLicenseeName,
          NORMAL, " of the following products:",
        );
        oConsole.fPrint(u"\u2502     ", faxListOutput(oLicense.asProductNames, "and", INFO, NORMAL));
      if asProductNamesInTrial:
        oConsole.fPrint(*(
          [
            u"\u2502 \u2219 "
          ] + faxListOutput(asProductNamesInTrial, "and", WARNING_INFO, WARNING)  + [
            WARNING, " ", len(asProductNamesInTrial) == 1 and "is" or "are", " not covered by a valid, active license but ",
            len(asProductNamesInTrial) == 1 and "it is in its" or "they are in their", " trial period.",
          ]
        ));
      if asUnlicensedProductNames:
        oConsole.fPrint(*(
          [
            u"\u2502 \u2219 "
          ] + faxListOutput(asUnlicensedProductNames, "and", ERROR_INFO, ERROR)  + [
            ERROR, " ", len(asProductNamesInTrial) == 1 and "is" or "are", " not covered by a valid, active license and ",
            len(asProductNamesInTrial) == 1 and "has exceeded its" or "have exceeded their", " trial period.",
          ]
        ));
    oConsole.fPrint(
      u"\u2514", sPadding = u"\u2500",
    );
    oConsole.fPrint();
  finally:
    oConsole.fUnlock();

  (asLicenseErrors, asLicenseWarnings) = mProductDetails.ftasGetLicenseErrorsAndWarnings();
  if asLicenseErrors:
    oConsole.fLock();
    try:
      oConsole.fPrint(ERROR, u"\u250C\u2500", ERROR_INFO, " Software license error ", ERROR, sPadding = u"\u2500");
      for sLicenseError in asLicenseErrors:
        oConsole.fPrint(ERROR, u"\u2502 ", ERROR_INFO, sLicenseError);
      oConsole.fPrint(ERROR, u"\u2514", sPadding = u"\u2500");
    finally:
      oConsole.fUnlock();
  if asLicenseWarnings:
    oConsole.fLock();
    try:
      oConsole.fPrint(WARNING, u"\u250C\u2500", WARNING_INFO, " Software license warning ", WARNING, sPadding = u"\u2500");
      for sLicenseWarning in asLicenseWarnings:
        oConsole.fPrint(WARNING, u"\u2502 ", WARNING_INFO, sLicenseWarning);
      oConsole.fPrint(WARNING, u"\u2514", sPadding = u"\u2500");
    finally:
      oConsole.fUnlock();
예제 #4
0
def fOutputVersionInformation(bCheckForUpdates,
                              bShowInstallationFolders,
                              dsAdditionalVersion_by_sName={}):
    # Read product details for rs and all modules it uses.
    aoProductDetails = mProductDetails.faoGetProductDetailsForAllLoadedModules(
    )
    o0MainProductDetails = mProductDetails.fo0GetProductDetailsForMainModule()
    oConsole.fLock()
    try:
        aoProductDetailsCheckedForUpdates = []
        aoProductDetailsSuccessfullyCheckedForUpdates = []
        if bCheckForUpdates:
            for oProductDetails in aoProductDetails:
                if oProductDetails.o0Repository is None:
                    continue
                aoProductDetailsCheckedForUpdates.append(oProductDetails)
                oConsole.fProgressBar(
                    len(aoProductDetailsCheckedForUpdates) * 1.0 /
                    len(aoProductDetails),
                    "Checking %s for updates..." %
                    oProductDetails.sProductName,
                )
                try:
                    oProductDetails.foGetLatestProductDetailsFromRepository()
                except mProductDetails.mExceptions.cProductDetailsException as oException:
                    oConsole.fOutput(
                        COLOR_ERROR,
                        CHAR_ERROR,
                        COLOR_NORMAL,
                        " Version check for ",
                        COLOR_INFO,
                        oProductDetails.sProductName,
                        COLOR_NORMAL,
                        " failed: ",
                        COLOR_INFO,
                        str(oException),
                    )
                else:
                    aoProductDetailsSuccessfullyCheckedForUpdates.append(
                        oProductDetails)
            if len(aoProductDetailsSuccessfullyCheckedForUpdates) == 0:
                oConsole.fOutput(
                    COLOR_WARNING,
                    CHAR_WARNING,
                    COLOR_NORMAL,
                    "Failed to get any product version information.",
                )
                oConsole.fOutput(
                    "  (This often indicates you are running a ",
                    COLOR_INFO,
                    "pre-release",
                    COLOR_NORMAL,
                    " version, or a version that is very ",
                    COLOR_INFO,
                    "out of date",
                    COLOR_NORMAL,
                    ").",
                )
                oConsole.fOutput(
                    "  To try and resolve this issue, please update this product to the latest",
                )
                oConsole.fOutput("  version and try again.", )

        if f0OutputLogo:
            f0OutputLogo()

        oConsole.fOutput(
            "┌───[",
            COLOR_HILITE,
            " Version information ",
            COLOR_NORMAL,
            "]",
            sPadding="─",
        )
        # Output the main product information first, then its dependencies alphabetically:
        if o0MainProductDetails:
            fOutputProductDetails(
                o0MainProductDetails,
                bIsMainProduct=True,
                bShowInstallationFolders=bShowInstallationFolders,
                bCheckForUpdates=bCheckForUpdates,
                bCheckForUpdatesSuccessful=o0MainProductDetails
                in aoProductDetailsSuccessfullyCheckedForUpdates,
            )
        doRemainingProductDetails_by_sName = dict([
            (oProductDetails.sProductName, oProductDetails)
            for oProductDetails in aoProductDetails
            if oProductDetails != o0MainProductDetails
        ])
        for sProductName in sorted(doRemainingProductDetails_by_sName.keys()):
            oProductDetails = doRemainingProductDetails_by_sName[sProductName]
            fOutputProductDetails(
                oProductDetails,
                bIsMainProduct=False,
                bShowInstallationFolders=bShowInstallationFolders,
                bCheckForUpdates=bCheckForUpdates,
                bCheckForUpdatesSuccessful=oProductDetails
                in aoProductDetailsSuccessfullyCheckedForUpdates,
            )
        asProductNames = (([o0MainProductDetails.sProductName]
                           if o0MainProductDetails else []) +
                          list(doRemainingProductDetails_by_sName.keys()))

        oConsole.fOutput(
            "│ ",
            CHAR_LIST,
            " ",
            COLOR_INFO,
            "Windows",
            COLOR_NORMAL,
            " version: ",
            COLOR_INFO,
            oSystemInfo.sOSName,
            COLOR_NORMAL,
            " release ",
            COLOR_INFO,
            oSystemInfo.sOSReleaseId,
            COLOR_NORMAL,
            ", build ",
            COLOR_INFO,
            oSystemInfo.sOSBuild,
            COLOR_NORMAL,
            " ",
            COLOR_INFO,
            oSystemInfo.sOSISA,
            COLOR_NORMAL,
            ".",
        )
        oConsole.fOutput(
            "│ ",
            CHAR_LIST,
            " ",
            COLOR_INFO,
            "Python",
            COLOR_NORMAL,
            " version: ",
            COLOR_INFO,
            str(platform.python_version()),
            COLOR_NORMAL,
            " ",
            COLOR_INFO,
            fsGetPythonISA(),
            COLOR_NORMAL,
            ".",
        )

        for (sName, sVersion) in dsAdditionalVersion_by_sName.items():
            oConsole.fOutput(
                "│ ",
                CHAR_LIST,
                " ",
                COLOR_INFO,
                sName,
                COLOR_NORMAL,
                " version: ",
                COLOR_INFO,
                sVersion,
                COLOR_NORMAL,
                ".",
            )

        oConsole.fOutput(
            "└",
            sPadding="─",
        )
    finally:
        oConsole.fUnlock()
예제 #5
0
def fMain(asArguments):
  global \
      gasAttachForProcessExecutableNames, \
      gasBinaryNamesThatAreAllowedToRunWithoutPageHeap, \
      gbQuiet, \
      gbVerbose, \
      guDetectedBugsCount, \
      guMaximumNumberOfBugs;
  
  # Make sure Windows and the Python binary are up to date; we don't want our users to unknowingly run outdated
  # software as this is likely to cause unexpected issues.
  fCheckPythonVersion("BugId", asTestedPythonVersions, "https://github.com/SkyLined/BugId/issues/new")
  if mWindowsAPI.oSystemInfo.sOSVersion != "10.0":
    oConsole.fPrint(ERROR, "Error: unfortunately BugId only runs on Windows 10 at this time.");
    os._exit(3);
  if mWindowsAPI.oSystemInfo.sOSISA == "x64" and mWindowsAPI.fsGetPythonISA() == "x86":
    oConsole.fLock();
    try:
      oConsole.fPrint(WARNING, u"\u250C\u2500", WARNING_INFO, " Warning ", WARNING, sPadding = u"\u2500");
      oConsole.fPrint(WARNING, u"\u2502 You are running a ", WARNING_INFO, "32-bit", WARNING, " version of Python on a ",
          WARNING_INFO, "64-bit", WARNING, " version of Windows.");
      oConsole.fPrint(WARNING, u"\u2502 BugId will not be able to debug 64-bit applications unless you run it in a 64-bit " +
          "version of Python.");
      oConsole.fPrint(WARNING, u"\u2502 If you experience any issues, use a 64-bit version of Python and try again.");
      oConsole.fPrint(WARNING, u"\u2514", sPadding = u"\u2500");
    finally:
      oConsole.fUnlock();
  
  # Show usage information if no arguments are provided:
  if len(asArguments) == 0:
    fPrintLogo();
    fPrintUsageInformation(ddxApplicationSettings_by_sKeyword.keys());
    oConsole.fCleanup();
    os._exit(0);
  
  # Parse all arguments until we encounter "--".
  sApplicationKeyword = None;
  sApplicationBinaryPath = None;
  auApplicationProcessIds = [];
  sUWPApplicationPackageName = None;
  sUWPApplicationId = None;
  asApplicationOptionalArguments = None;
  sApplicationISA = None;
  bRepeat = False;
  uNumberOfRepeats = None;
  bCheckForUpdates = False;
  dxUserProvidedConfigSettings = {};
  asAdditionalLocalSymbolPaths = [];
  bFast = False;
  while asArguments:
    sArgument = asArguments.pop(0);
    if sArgument == "--":
      if len(auApplicationProcessIds) > 0:
      # The rest of the arguments are to be passed to the application
        oConsole.fPrint(ERROR, "- You cannot provide process ids and application arguments.");
        oConsole.fCleanup();
        os._exit(2);
      asApplicationOptionalArguments = asArguments;
      break;
    elif sArgument in ["-q", "/q"]:
      gbQuiet = True;
    elif sArgument in ["-v", "/v"]:
      gbVerbose = True;
    elif sArgument in ["-f", "/f"]:
      bFast = True;
    elif sArgument in ["-r", "/r"]:
      bRepeat = True;
    elif sArgument in ["-c", "/c"]:
      guMaximumNumberOfBugs = guDefaultCollateralMaximumNumberOfBugs;
    elif sArgument in ["-?", "/?", "-h", "/h"]:
      fPrintLogo();
      fPrintUsageInformation(ddxApplicationSettings_by_sKeyword.keys());
      oConsole.fCleanup();
      os._exit(0);
    elif sArgument.startswith("--"):
      if "=" in sArgument:
        sSettingName, sValue = sArgument[2:].split("=", 1);
      else:
        # "--bFlag" is an alias for "--bFlag=true"
        sSettingName = sArgument[2:];
        sValue = None;
      
      if sSettingName in ["pid", "pids"]:
        if not sValue:
          oConsole.fPrint(ERROR, "- You must provide at least one process id.");
          oConsole.fCleanup();
          os._exit(2);
        if sApplicationBinaryPath is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an application binary and process ids.");
          oConsole.fCleanup();
          os._exit(2);
        if sUWPApplicationPackageName is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an UWP application package name and process ids.");
          oConsole.fCleanup();
          os._exit(2);
        auApplicationProcessIds += [long(x) for x in sValue.split(",")];
      elif sSettingName in ["uwp", "uwp-app"]:
        if not sValue:
          oConsole.fPrint(ERROR, "- You must provide UWP application details.");
          oConsole.fCleanup();
          os._exit(2);
        if sUWPApplicationPackageName is not None:
          oConsole.fPrint(ERROR, "- You cannot provide UWP application details more than once.");
          oConsole.fCleanup();
          os._exit(2);
        if sApplicationBinaryPath is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an application binary and UWP application details.");
          oConsole.fCleanup();
          os._exit(2);
        if len(auApplicationProcessIds) > 0:
          oConsole.fPrint(ERROR, "- You cannot provide process ids and UWP application details.");
          oConsole.fCleanup();
          os._exit(2);
        if "!" not in sValue:
          oConsole.fPrint(ERROR, "- Please provide UWP application details in the form ", ERROR_INFO, sSettingName, \
              "=<package name>!<application id>", ERROR, ".");
          oConsole.fCleanup();
          os._exit(2);
        sUWPApplicationPackageName, sUWPApplicationId = sValue.split("!", 1);
      elif sSettingName in ["help"]:
        fPrintLogo();
        fPrintUsageInformation(ddxApplicationSettings_by_sKeyword.keys());
        oConsole.fCleanup();
        os._exit(0);
      elif sSettingName in ["version", "check-for-updates"]:
        fPrintVersionInformation(
          bCheckForUpdates = True,
          bCheckAndShowLicenses = True,
          bShowInstallationFolders = True,
        );
        oConsole.fCleanup();
        os._exit(0);
      elif sSettingName in ["isa", "cpu"]:
        if not sValue:
          oConsole.fPrint(ERROR, "- You must provide an Instruction Set Architecture.");
          oConsole.fCleanup();
          os._exit(2);
        if sValue not in ["x86", "x64"]:
          oConsole.fPrint(ERROR, "- Unknown Instruction Set Architecture ", repr(sValue));
          oConsole.fCleanup();
          os._exit(2);
        sApplicationISA = sValue;
      elif sSettingName in ["quiet", "silent"]:
        if sValue is None or sValue.lower() == "true":
          gbQuiet = True;
        elif sValue.lower() == "false":
          gbQuiet = False;
        else:
          oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \
              " must be \"true\" or \"false\".");
      elif sSettingName in ["verbose", "debug"]:
        if sValue is None or sValue.lower() == "true":
          gbVerbose = True;
        elif sValue.lower() == "false":
          gbVerbose = False;
        else:
          oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \
              " must be \"true\" or \"false\".");
      elif sSettingName in ["fast", "quick"]:
        if sValue is None or sValue.lower() == "true":
          bFast = True;
        elif sValue.lower() == "false":
          bFast = False;
        else:
          oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \
              " must be \"true\" or \"false\".");
      elif sSettingName in ["forever"]:
        if sValue is None or sValue.lower() == "true":
          bRepeat = True;
        elif sValue.lower() == "false":
          bRepeat = False;
        else:
          oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \
              " must be \"true\" or \"false\".");
      elif sSettingName in ["repeat"]:
        bRepeat = sValue is None or sValue.lower() != "false";
        if bRepeat and sValue is not None:
          try:
            uNumberOfRepeats = long(sValue);
            if uNumberOfRepeats < 2:
              uNumberOfRepeats = None;
            elif str(uNumberOfRepeats) != sValue:
              uNumberOfRepeats = None;
          except:
            pass;
          if uNumberOfRepeats is None:
            oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \
                " must be an integer larger than 1 or \"false\".");
      elif sSettingName in ["collateral"]:
        if sValue is None:
          guMaximumNumberOfBugs = guDefaultCollateralMaximumNumberOfBugs;
        else:
          # -- collateral=1 means one collateral bug in addition to the first bug.
          guMaximumNumberOfBugs = long(sValue) + 1;
      elif sSettingName in ["symbols"]:
        if sValue is None or not mFileSystem2.foGetFolder(sValue):
          oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \
              " must be a valid path.");
        asAdditionalLocalSymbolPaths.append(sValue);
      elif sSettingName in ["test-internal-error", "internal-error-test"]:
        raise Exception("Testing internal error");
      else:
        if not sValue:
          oConsole.fPrint(ERROR, "- You cannot provide an argument (", ERROR_INFO, "--", sSettingName, ERROR, \
              ") without a value.");
          oConsole.fCleanup();
          os._exit(2);
        try:
          xValue = json.loads(sValue);
        except ValueError as oError:
          oConsole.fPrint(ERROR, "- Cannot decode argument JSON value --", ERROR_INFO, sSettingName, ERROR, "=", \
              ERROR_INFO, sValue, ERROR, ": ", ERROR_INFO, " ".join(oError.args), ERROR, ".");
          oConsole.fCleanup();
          os._exit(2);
        # User provided config settings must be applied after any keyword specific config settings:
        dxUserProvidedConfigSettings[sSettingName] = xValue;
    elif sArgument in ddxApplicationSettings_by_sKeyword:
      if sApplicationKeyword is not None:
        oConsole.fPrint(ERROR, "- You cannot provide multiple application keywords.");
        oConsole.fCleanup();
        os._exit(2);
      sApplicationKeyword = sArgument;
    elif sArgument[-1] == "?":
      sApplicationKeyword = sArgument[:-1];
      dxApplicationSettings = ddxApplicationSettings_by_sKeyword.get(sApplicationKeyword);
      if not dxApplicationSettings:
        oConsole.fPrint(ERROR, "- Unknown application keyword ", ERROR_INFO, sApplicationKeyword, ERROR, ".");
        oConsole.fCleanup();
        os._exit(2);
      fPrintApplicationKeyWordHelp(sApplicationKeyword, dxApplicationSettings);
      oConsole.fCleanup();
      os._exit(0);
    else:
      if sApplicationBinaryPath is not None:
        oConsole.fLock();
        try:
          oConsole.fPrint(ERROR, "- You cannot provide multiple application binaries.");
          oConsole.fPrint(ERROR, "  (Did you perhaps forget to put ", ERROR_INFO, "--", ERROR, \
              " before the start of the application arguments?)");
        finally:
          oConsole.fUnlock();
        oConsole.fCleanup();
        os._exit(2);
      if len(auApplicationProcessIds) > 0:
        oConsole.fPrint(ERROR, "- You cannot provide process ids and an application binary.");
        oConsole.fCleanup();
        os._exit(2);
      if sUWPApplicationPackageName is not None:
        oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name and a binary.");
        oConsole.fCleanup();
        os._exit(2);
      sApplicationBinaryPath = sArgument;
  
  if bFast:
    gbQuiet = True;
    dxUserProvidedConfigSettings["bGenerateReportHTML"] = False;
    dxUserProvidedConfigSettings["asSymbolServerURLs"] = [];
    dxUserProvidedConfigSettings["cBugId.bUse_NT_SYMBOL_PATH"] = False;
  
  dsApplicationURLTemplate_by_srSourceFilePath = {};
  
  fSetup = None; # Function specific to a keyword application, used to setup stuff before running.
  fCleanup = None; # Function specific to a keyword application, used to cleanup stuff before & after running.
  if sApplicationKeyword:
    dxApplicationSettings = ddxApplicationSettings_by_sKeyword.get(sApplicationKeyword);
    if not dxApplicationSettings:
      oConsole.fPrint(ERROR, "- Unknown application keyword ", ERROR_INFO, sApplicationKeyword, ERROR, ".");
      oConsole.fCleanup();
      os._exit(2);
    fSetup = dxApplicationSettings.get("fSetup");
    fCleanup = dxConfig["bCleanup"] and dxApplicationSettings.get("fCleanup");
    # Get application binary/UWP package name/process ids as needed:
    if "sBinaryPath" in dxApplicationSettings:
      # This application is started from the command-line.
      if auApplicationProcessIds:
        oConsole.fPrint(ERROR, "- You cannot provide process ids for application keyword ", ERROR_INFO, \
            sApplicationKeyword, ERROR, ".");
        oConsole.fCleanup();
        os._exit(2);
      if sUWPApplicationPackageName:
        oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name for application keyword ", \
            ERROR_INFO, sApplicationKeyword, ERROR, ".");
        oConsole.fCleanup();
        os._exit(2);
      if sApplicationBinaryPath is None:
        sApplicationBinaryPath = dxApplicationSettings["sBinaryPath"];
        if sApplicationBinaryPath is None:
          oConsole.fPrint(ERROR, "- The main application binary for ", ERROR_INFO, sApplicationKeyword, \
              ERROR, " could not be detected on your system.");
          oConsole.fPrint(ERROR, "  Please provide the path to this binary in the arguments.");
          oConsole.fCleanup();
          os._exit(4);
    elif "dxUWPApplication" in dxApplicationSettings:
      dxUWPApplication = dxApplicationSettings["dxUWPApplication"];
      # This application is started as a Universal Windows Platform application.
      if sApplicationBinaryPath:
        oConsole.fPrint(ERROR, "- You cannot provide an application binary for application keyword ", \
            ERROR_INFO, sApplicationKeyword, ERROR, ".");
        oConsole.fCleanup();
        os._exit(2);
      if auApplicationProcessIds:
        oConsole.fPrint(ERROR, "- You cannot provide process ids for application keyword ", ERROR_INFO, \
            sApplicationKeyword, ERROR, ".");
        oConsole.fCleanup();
        os._exit(2);
      sUWPApplicationPackageName = dxUWPApplication["sPackageName"];
      sUWPApplicationId = dxUWPApplication["sId"];
    elif not auApplicationProcessIds:
      # This application is attached to.
      oConsole.fPrint(ERROR, "- You must provide process ids for application keyword ", \
          ERROR_INFO, sApplicationKeyword, ERROR, ".");
      oConsole.fCleanup();
      os._exit(2);
    elif asApplicationOptionalArguments:
      # Cannot provide arguments if we're attaching to processes
      oConsole.fPrint(ERROR, "- You cannot provide arguments for application keyword ", \
          ERROR_INFO, sApplicationKeyword, ERROR, ".");
      oConsole.fCleanup();
      os._exit(2);
    if "asApplicationAttachForProcessExecutableNames" in dxApplicationSettings:
      gasAttachForProcessExecutableNames = dxApplicationSettings["asApplicationAttachForProcessExecutableNames"];
    # Get application arguments;
    if "fasGetStaticArguments" in dxApplicationSettings:
      fasGetApplicationStaticArguments = dxApplicationSettings["fasGetStaticArguments"];
      asApplicationStaticArguments = fasGetApplicationStaticArguments(bForHelp = False);
    else:
      asApplicationStaticArguments = [];
    if asApplicationOptionalArguments is None and "fasGetOptionalArguments" in dxApplicationSettings:
      fasGetApplicationOptionalArguments = dxApplicationSettings["fasGetOptionalArguments"];
      asApplicationOptionalArguments = fasGetApplicationOptionalArguments(bForHelp = False);
    asApplicationArguments = asApplicationStaticArguments + asApplicationOptionalArguments;
    # Apply application specific settings
    if dxApplicationSettings.get("dxConfigSettings"):
      dxApplicationConfigSettings = dxApplicationSettings["dxConfigSettings"];
      if gbVerbose:
        oConsole.fPrint("* Applying application specific configuration for %s:" % sApplicationKeyword);
      for (sSettingName, xValue) in dxApplicationConfigSettings.items():
        if sSettingName not in dxUserProvidedConfigSettings:
          # Apply and show result indented or errors.
          if not fbApplyConfigSetting(sSettingName, xValue, [None, "  "][gbVerbose]):
            os._exit(2);
      if gbVerbose:
        oConsole.fPrint();
    # Apply application specific source settings
    if "dsURLTemplate_by_srSourceFilePath" in dxApplicationSettings:
      dsApplicationURLTemplate_by_srSourceFilePath = dxApplicationSettings["dsURLTemplate_by_srSourceFilePath"];
    # If not ISA is specified, apply the application specific ISA (if any).
    if not sApplicationISA and "sISA" in dxApplicationSettings:
      sApplicationISA = dxApplicationSettings["sISA"];
    if "asBinaryNamesThatAreAllowedToRunWithoutPageHeap" in dxApplicationSettings:
      gasBinaryNamesThatAreAllowedToRunWithoutPageHeap += [
        sBinaryName.lower() for sBinaryName in dxApplicationSettings["asBinaryNamesThatAreAllowedToRunWithoutPageHeap"]
      ];
  elif (auApplicationProcessIds or sUWPApplicationPackageName or sApplicationBinaryPath):
    # There are no static arguments if there is no application keyword, only the user-supplied optional arguments
    # are used if they are supplied:
    asApplicationArguments = asApplicationOptionalArguments or [];
  else:
    oConsole.fLock();
    try:
      oConsole.fPrint(ERROR, "- You must provide something to debug. This can be either one or more process");
      oConsole.fPrint(ERROR, "  ids, an application command-line or an UWP application package name.");
      oConsole.fPrint("Run \"", INFO, "BugId -h", NORMAL, "\" for help on command-line arguments.");
    finally:
      oConsole.fUnlock();
    oConsole.fCleanup();
    os._exit(2);
  
  # Apply user provided settings:
  for (sSettingName, xValue) in dxUserProvidedConfigSettings.items():
    # Apply and show result or errors:
    if not fbApplyConfigSetting(sSettingName, xValue, [None, ""][gbVerbose]):
      os._exit(2);
  
  # Check if cdb.exe is found:
  sCdbISA = sApplicationISA or cBugId.sOSISA;
  if not cBugId.fbCdbFound(sCdbISA):
    oConsole.fLock();
    try:
      oConsole.fPrint(ERROR, "- BugId depends on ", ERROR_INFO, "Debugging Tools for Windows", ERROR, " which was not found.");
      oConsole.fPrint();
      oConsole.fPrint("To install, download the Windows 10 SDK installer at:");
      oConsole.fPrint();
      oConsole.fPrint("  ", INFO, "https://developer.microsoft.com/en-US/windows/downloads/windows-10-sdk");
      oConsole.fPrint();
      oConsole.fPrint("After downloading, run the installer. You can deselect all other features");
      oConsole.fPrint("of the SDK before installation; only ", INFO, "Debugging Tools for Windows", NORMAL, " is required.");
      oConsole.fPrint();
      oConsole.fPrint("Once you have completed these steps, please try again.");
    finally:
      oConsole.fUnlock();
    oConsole.fCleanup();
    os._exit(2);
  
  # Check license
  (asLicenseErrors, asLicenseWarnings) = mProductDetails.ftasGetLicenseErrorsAndWarnings();
  if asLicenseErrors:
    oConsole.fLock();
    try:
      oConsole.fPrint(ERROR, u"\u250C\u2500", ERROR_INFO, " Software license error ", ERROR, sPadding = u"\u2500");
      for sLicenseError in asLicenseErrors:
        oConsole.fPrint(ERROR, u"\u2502 ", ERROR_INFO, sLicenseError);
      oConsole.fPrint(ERROR, u"\u2514", sPadding = u"\u2500");
    finally:
      oConsole.fUnlock();
    os._exit(5);
  if asLicenseWarnings:
    oConsole.fLock();
    try:
      oConsole.fPrint(WARNING, u"\u250C\u2500", WARNING_INFO, " Software license warning ", WARNING, sPadding = u"\u2500");
      for sLicenseWarning in asLicenseWarnings:
        oConsole.fPrint(WARNING, u"\u2502 ", WARNING_INFO, sLicenseWarning);
      oConsole.fPrint(WARNING, u"\u2514", sPadding = u"\u2500");
    finally:
      oConsole.fUnlock();
  
  if bRepeat:
    sValidStatisticsFileName = mFileSystem2.fsGetValidName("Reproduction statistics.txt");
  uRunCounter = 0;
  while 1: # Will only loop if bRepeat is True
    nStartTimeInSeconds = time.clock();
    if fSetup:
      # Call setup before the application is started. Argument is boolean value indicating if this is the first time
      # the function is being called.
      oConsole.fStatus("* Applying special application configuration settings...");
      fSetup(bFirstRun = uRunCounter == 0);
    uRunCounter += 1;
    oConsole.fLock();
    try:
      if sApplicationBinaryPath:
        # make the binary path absolute because relative paths don't work.
        sApplicationBinaryPath = os.path.abspath(sApplicationBinaryPath);
        if not gbQuiet:
          asCommandLine = [sApplicationBinaryPath] + asApplicationArguments;
          oConsole.fPrint("* Command line: ", INFO, " ".join(asCommandLine));
        oConsole.fStatus("* The debugger is starting the application...");
      else:
        if auApplicationProcessIds:
          asProcessIdsOutput = [];
          for uApplicationProcessId in auApplicationProcessIds:
            if asProcessIdsOutput: asProcessIdsOutput.append(", ");
            asProcessIdsOutput.extend([INFO, str(uApplicationProcessId), NORMAL]);
          oConsole.fPrint("* Running process ids: ", INFO, *asProcessIdsOutput);
        if sUWPApplicationPackageName:
          if not gbQuiet:
            if asApplicationArguments:
              oConsole.fPrint("* UWP application id: ", INFO, sUWPApplicationId, NORMAL, ", package name: ", INFO, \
                  sUWPApplicationPackageName, NORMAL, ", Arguments: ", INFO, " ".join(asApplicationArguments));
            else:
              oConsole.fPrint("* UWP application id: ", INFO, sUWPApplicationId, NORMAL, ", package name: ", INFO, \
                  sUWPApplicationPackageName);
        if not sUWPApplicationPackageName:
          oConsole.fStatus("* The debugger is attaching to running processes of the application...");
        elif auApplicationProcessIds:
          oConsole.fStatus("* The debugger is attaching to running processes and starting the application...");
        else:
          oConsole.fStatus("* The debugger is starting the application...");
    finally:
      oConsole.fUnlock();
    asLocalSymbolPaths = dxConfig["asLocalSymbolPaths"] or [];
    if asAdditionalLocalSymbolPaths:
      asLocalSymbolPaths += asAdditionalLocalSymbolPaths;
    oBugId = cBugId(
      sCdbISA = sCdbISA,
      sApplicationBinaryPath = sApplicationBinaryPath or None,
      auApplicationProcessIds = auApplicationProcessIds or None,
      sUWPApplicationPackageName = sUWPApplicationPackageName or None,
      sUWPApplicationId = sUWPApplicationId or None,
      asApplicationArguments = asApplicationArguments,
      asLocalSymbolPaths = asLocalSymbolPaths or None,
      asSymbolCachePaths = dxConfig["asSymbolCachePaths"], 
      asSymbolServerURLs = dxConfig["asSymbolServerURLs"],
      dsURLTemplate_by_srSourceFilePath = dsApplicationURLTemplate_by_srSourceFilePath,
      bGenerateReportHTML = dxConfig["bGenerateReportHTML"],
      uProcessMaxMemoryUse = dxConfig["uProcessMaxMemoryUse"],
      uTotalMaxMemoryUse = dxConfig["uTotalMaxMemoryUse"],
      uMaximumNumberOfBugs = guMaximumNumberOfBugs,
    );
    oBugId.fAddEventCallback("Application resumed", fApplicationResumedCallback);
    oBugId.fAddEventCallback("Application running", fApplicationRunningCallback);
    oBugId.fAddEventCallback("Application suspended", fApplicationSuspendedCallback);
    oBugId.fAddEventCallback("Application debug output", fApplicationDebugOutputCallback);
    oBugId.fAddEventCallback("Application stderr output", fApplicationStdErrOutputCallback);
    oBugId.fAddEventCallback("Application stdout output", fApplicationStdOutOutputCallback);
    oBugId.fAddEventCallback("Bug report", fBugReportCallback);
    oBugId.fAddEventCallback("Cdb stderr output", fCdbStdErrOutputCallback);
    if gbVerbose:
      oBugId.fAddEventCallback("Cdb stdin input", fCdbStdInInputCallback);
      oBugId.fAddEventCallback("Cdb stdout output", fCdbStdOutOutputCallback);
      oBugId.fAddEventCallback("Log message", fLogMessageCallback);
    oBugId.fAddEventCallback("Failed to apply application memory limits", fFailedToApplyApplicationMemoryLimitsCallback);
    oBugId.fAddEventCallback("Failed to apply process memory limits", fFailedToApplyProcessMemoryLimitsCallback);
    oBugId.fAddEventCallback("Failed to debug application", fFailedToDebugApplicationCallback);
    oBugId.fAddEventCallback("Internal exception", fInternalExceptionCallback);
    oBugId.fAddEventCallback("License warnings", fLicenseWarningsCallback);
    oBugId.fAddEventCallback("License errors", fLicenseErrorsCallback);
    oBugId.fAddEventCallback("Page heap not enabled", fPageHeapNotEnabledCallback);
    oBugId.fAddEventCallback("Cdb ISA not ideal", fCdbISANotIdealCallback);
    oBugId.fAddEventCallback("Process attached", fProcessAttachedCallback);
    oBugId.fAddEventCallback("Process started", fProcessStartedCallback);
    oBugId.fAddEventCallback("Process terminated", fProcessTerminatedCallback);

    if dxConfig["nApplicationMaxRunTimeInSeconds"] is not None:
      oBugId.foSetTimeout("Maximum application runtime", dxConfig["nApplicationMaxRunTimeInSeconds"], \
          fApplicationMaxRunTimeCallback);
    if dxConfig["bExcessiveCPUUsageCheckEnabled"] and dxConfig["nExcessiveCPUUsageCheckInitialTimeoutInSeconds"]:
      oBugId.fSetCheckForExcessiveCPUUsageTimeout(dxConfig["nExcessiveCPUUsageCheckInitialTimeoutInSeconds"]);
    guDetectedBugsCount = 0;
    oBugId.fStart();
    oBugId.fWait();
    if gbAnErrorOccured:
      if fCleanup:
        # Call cleanup after runnning the application, before exiting BugId
        oConsole.fStatus("* Cleaning up application state...");
        fCleanup();
      oConsole.fCleanup();
      os._exit(3);
    if guDetectedBugsCount == 0:
      oConsole.fPrint(u"\u2500\u2500 The application terminated without a bug being detected ", sPadding = u"\u2500");
      gduNumberOfRepros_by_sBugIdAndLocation.setdefault("No crash", 0);
      gduNumberOfRepros_by_sBugIdAndLocation["No crash"] += 1;
    if gbVerbose:
      oConsole.fPrint("  Application time: %s seconds" % (long(oBugId.fnApplicationRunTimeInSeconds() * 1000) / 1000.0));
      nOverheadTimeInSeconds = time.clock() - nStartTimeInSeconds - oBugId.fnApplicationRunTimeInSeconds();
      oConsole.fPrint("  BugId overhead:   %s seconds" % (long(nOverheadTimeInSeconds * 1000) / 1000.0));
    if uNumberOfRepeats is not None:
      uNumberOfRepeats -= 1;
      if uNumberOfRepeats == 0:
        bRepeat = False;
    if not bRepeat:
      if fCleanup:
        # Call cleanup after runnning the application, before exiting BugId
        oConsole.fStatus("* Cleaning up application state...");
        fCleanup();
      oConsole.fCleanup();
      os._exit(guDetectedBugsCount > 0 and 1 or 0);
    sStatistics = "";
    auOrderedNumberOfRepros = sorted(list(set(gduNumberOfRepros_by_sBugIdAndLocation.values())));
    auOrderedNumberOfRepros.reverse();
    for uNumberOfRepros in auOrderedNumberOfRepros:
      for sBugIdAndLocation in gduNumberOfRepros_by_sBugIdAndLocation.keys():
        if gduNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] == uNumberOfRepros:
          sStatistics += "%d \xD7 %s (%d%%)\r\n" % (uNumberOfRepros, str(sBugIdAndLocation), \
              round(100.0 * uNumberOfRepros / uRunCounter));
    if dxConfig["sReportFolderPath"] is not None:
      sStatisticsFilePath = os.path.join(dxConfig["sReportFolderPath"], sValidStatisticsFileName);
    else:
      sStatisticsFilePath = sValidStatisticsFileName;
    oStatisticsFile = None;
    try:
      oStatisticsFile = mFileSystem2.foGetOrCreateFile(sStatisticsFilePath);
      oStatisticsFile.fWrite(sStatistics);
    except Exception as oException:
      oConsole.fPrint("  Statistics:       ", ERROR, "Cannot be saved (", ERROR_INFO, str(oException), ERROR, ")");
    else:
      oConsole.fPrint("  Statistics:       ", INFO, sStatisticsFilePath, NORMAL, " (%d bytes)" % len(sStatistics));
    if oStatisticsFile:
      oStatisticsFile.fClose();
    oConsole.fPrint(); # and loop
  raise AssertionError("Not reached!");