Ejemplo n.º 1
0
def fBugReportCallback(oBugId, oBugReport):
  global guDetectedBugsCount, \
         guMaximumNumberOfBugs, \
         gduNumberOfRepros_by_sBugIdAndLocation;
  guDetectedBugsCount += 1;
  oConsole.fLock();
  try:
    oConsole.fPrint(u"\u250C\u2500 ", HILITE, "A bug was detect in the application ", NORMAL, sPadding = u"\u2500");
    if oBugReport.sBugLocation:
      oConsole.fPrint(u"\u2502 Id @ Location:    ", INFO, oBugReport.sId, NORMAL, " @ ", INFO, oBugReport.sBugLocation);
      sBugIdAndLocation = "%s @ %s" % (oBugReport.sId, oBugReport.sBugLocation);
    else:
      oConsole.fPrint(u"\u2502 Id:               ", INFO, oBugReport.sId);
      sBugIdAndLocation = oBugReport.sId;
    gduNumberOfRepros_by_sBugIdAndLocation.setdefault(sBugIdAndLocation, 0);
    gduNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] += 1;
    if oBugReport.sBugSourceLocation:
      oConsole.fPrint(u"\u2502 Source:           ", INFO, oBugReport.sBugSourceLocation);
    oConsole.fPrint(u"\u2502 Description:      ", INFO, oBugReport.sBugDescription);
    oConsole.fPrint(u"\u2502 Security impact:  ", INFO, oBugReport.sSecurityImpact);
    oConsole.fPrint(u"\u2502 Version:          ", NORMAL, oBugReport.asVersionInformation[0]); # The process' binary.
    for sVersionInformation in oBugReport.asVersionInformation[1:]: # There may be two if the crash was in a
      oConsole.fPrint(u"\u2502                   ", NORMAL, sVersionInformation); # different binary (e.g. a .dll)
    if dxConfig["bGenerateReportHTML"]:
      # Use a report file name base on the BugId.
      sDesiredReportFileName = "%s.html" % sBugIdAndLocation;
      # In collateral mode, we will number the reports so you know in which order bugs were reported.
      if guMaximumNumberOfBugs > 1:
        sDesiredReportFileName = "#%d %s" % (guDetectedBugsCount, sDesiredReportFileName);
      # Translate characters that are not valid in file names.
      sValidReportFileName = mFileSystem.fsValidName(sDesiredReportFileName, bUnicode = \
          dxConfig["bUseUnicodeReportFileNames"]);
      if dxConfig["sReportFolderPath"] is not None:
        sReportFilePath = mFileSystem.fsPath(dxConfig["sReportFolderPath"], sValidReportFileName);
      else:
        sReportFilePath = mFileSystem.fsPath(sValidReportFileName);
      eWriteDataToFileResult = mFileSystem.feWriteDataToFile(
        oBugReport.sReportHTML,
        sReportFilePath,
        fbRetryOnFailure = lambda: False,
      );
      if eWriteDataToFileResult:
        oConsole.fPrint(u"\u2502 Bug report:       ", ERROR, "Cannot be saved (", \
            ERROR_INFO, repr(eWriteDataToFileResult), ERROR, ")");
      else:
        oConsole.fPrint(u"\u2502 Bug report:       ", NORMAL, sValidReportFileName,  \
            " (%d bytes)" % len(oBugReport.sReportHTML));
    oConsole.fPrint(u"\u2514", sPadding = u"\u2500");
  finally:
    oConsole.fUnlock();
Ejemplo n.º 2
0
import codecs, os, sys;
sys.stdout = codecs.getwriter("cp437")(sys.stdout, "replace");

sModuleFolderPath = os.path.dirname(os.path.abspath(__file__));
sBaseFolderPath = os.path.dirname(sModuleFolderPath);
sys.path.extend([
  sBaseFolderPath,
  sModuleFolderPath,
  os.path.join(sModuleFolderPath, "modules"),
]);

import mFileSystem;

sTempFolderPath = mFileSystem.fsPath(os.environ["TEMP"]);
sSpecialChars = 'test[[[\0\r\n"<>\\/?*:|]]]';

sSpecialCharsPath = mFileSystem.fsPath(sTempFolderPath, sSpecialChars);

sCWD = os.getcwdu().rstrip("\\");
sCWDDrive, sCWDPath = os.path.splitdrive(sCWD);
print "* Testing fsPath...";
dsPathTests = {
  r".":                   r"\\?\%s" % sCWD,
  r"x":                   r"\\?\%s\x" % sCWD,
  r"x\y":                 r"\\?\%s\x\y" % sCWD,
  r"%sx\y" % sCWDDrive:   r"\\?\%s\x\y" % sCWD,
  r"%s\x\y" % sCWDDrive:  r"\\?\%s\x\y" % sCWDDrive,
  r"x:\y\z":              r"\\?\x:\y\z",
  r"\\?\x:\y\z":          r"\\?\x:\y\z",
  r"\\x\y\z":             r"\\?\UNC\x\y\z",
  r"\\?\UNC\x\y\z":       r"\\?\UNC\x\y\z",
Ejemplo n.º 3
0
def fTest(
    sISA,
    axCommandLineArguments,
    asExpectedBugIdAndLocations,
    sExpectedFailedToDebugApplicationErrorMessage=None,
    bRunInShell=False,
    sApplicationBinaryPath=None,
    uMaximumNumberOfBugs=2,
    bExcessiveCPUUsageChecks=False,
):
    global gbTestFailed
    if gbTestFailed:
        return
    asApplicationArguments = axCommandLineArguments and [
        isinstance(x, str) and x or x < 10 and ("%d" % x) or ("0x%X" % x)
        for x in axCommandLineArguments
    ] or []
    if sApplicationBinaryPath is None:
        sApplicationBinaryPath = dsBinaries_by_sISA[sISA]
    asCommandLine = [sApplicationBinaryPath] + asApplicationArguments
    sFailedToDebugApplicationErrorMessage = None
    if sExpectedFailedToDebugApplicationErrorMessage:
        sTestDescription = "%s => %s" % (
            "Running %s" % sApplicationBinaryPath,
            repr(sExpectedFailedToDebugApplicationErrorMessage))
    else:
        sTestDescription = "%s %s%s => %s" % (
          sISA,
          " ".join(asApplicationArguments), \
          bRunInShell and " (in child process)" or "",
          asExpectedBugIdAndLocations and " => ".join(asExpectedBugIdAndLocations) or "no bugs")

    sTestBinaryName = os.path.basename(sApplicationBinaryPath).lower()

    if bRunInShell:
        asApplicationArguments = ["/C", sApplicationBinaryPath
                                  ] + asApplicationArguments
        sApplicationBinaryPath = dsComSpec_by_sISA[sISA]

    fSetTitle(sTestDescription)
    if gbDebugStartFinish:
        fOutput("* Started %s" % sTestDescription)
    else:
        fOutput("* %s" % sTestDescription, bCRLF=False)

    asLog = []

    def fCdbStdInInputCallback(oBugId, sInput):
        if gbShowCdbIO: fOutput("stdin<%s" % sInput)
        asLog.append("stdin<%s" % sInput)

    def fCdbStdOutOutputCallback(oBugId, sOutput):
        if gbShowCdbIO: fOutput("stdout>%s" % sOutput)
        asLog.append("stdout>%s" % sOutput)

    def fCdbStdErrOutputCallback(oBugId, sOutput):
        if gbShowCdbIO: fOutput("stderr>%s" % sOutput)
        asLog.append("stderr>%s" % sOutput)


#    asLog.append("log>%s%s" % (sMessage, sData and " (%s)" % sData or ""));

    def fApplicationDebugOutputCallback(oBugId, oProcess, bIsMainProcess,
                                        asOutput):
        bFirstLine = True
        for sOutput in asOutput:
            sLogLine = "%s process %d/0x%X (%s): %s>%s" % (bIsMainProcess and "Main" or "Sub", oProcess.uId, \
                oProcess.uId, oProcess.sBinaryName, bFirstLine and "debug" or "     ", sOutput)
            if gbShowApplicationIO: fOutput(sLogLine)
            asLog.append(sLogLine)
            bFirstLine = False

    def fApplicationStdErrOutputCallback(oBugId, oConsoleProcess,
                                         bIsMainProcess, sOutput):
        # This is always a main process
        sLogLine = "%s process %d/0x%X (%s): stderr> %s" % (bIsMainProcess and "Main" or "Sub", oConsoleProcess.uId, \
            oConsoleProcess.uId, oConsoleProcess.sBinaryName, sOutput)
        if gbShowApplicationIO: fOutput(sLogLine)
        asLog.append(sLogLine)

    def fApplicationStdOutOutputCallback(oBugId, oConsoleProcess,
                                         bIsMainProcess, sOutput):
        # This is always a main process
        sLogLine = "%s process %d/0x%X (%s): stdout> %s" % (bIsMainProcess and "Main" or "Sub", oConsoleProcess.uId, \
            oConsoleProcess.uId, oConsoleProcess.sBinaryName, sOutput)
        if gbShowApplicationIO: fOutput(sLogLine)
        asLog.append(sLogLine)

    def fApplicationSuspendedCallback(oBugId, sReason):
        asLog.append("Application suspended (%s)" % sReason)

    def fApplicationResumedCallback(oBugId):
        asLog.append("Application resumed")

    def fApplicationRunningCallback(oBugId):
        asLog.append("Application running")

    def fFailedToDebugApplicationCallback(oBugId, sErrorMessage):
        global gbTestFailed
        if sExpectedFailedToDebugApplicationErrorMessage == sErrorMessage:
            return
        gbTestFailed = True
        if not gbShowCdbIO:
            for sLine in asLog:
                fOutput(sLine)
        fOutput("- Failed test: %s" % sTestDescription)
        if sExpectedFailedToDebugApplicationErrorMessage:
            fOutput("  Expected:    %s" %
                    repr(sExpectedFailedToDebugApplicationErrorMessage))
        else:
            fOutput("  BugId unexpectedly failed to debug the application")
        fOutput("  Error:       %s" % repr(sErrorMessage))
        oBugId.fStop()

    def fFailedToApplyMemoryLimitsCallback(oBugId, oProcess):
        global gbTestFailed
        gbTestFailed = True
        if not gbShowCdbIO:
            for sLine in asLog:
                fOutput(sLine)
        fOutput("- Failed to apply memory limits to process %d/0x%X (%s: %s) for test: %s" % (oProcess.uId, \
            oProcess.uId, oProcess.sBinaryName, oProcess.sCommandLine, sTestDescription))
        oBugId.fStop()

    def fFinishedCallback(oBugId):
        if gbShowCdbIO: fOutput("Finished")
        asLog.append("Finished")

    def fLicenseWarningsCallback(oBugId, asLicenseWarnings):
        global gbLicenseWarningsShown
        if not gbLicenseWarningsShown:
            fOutput("@" * 80)
            for sLicenseWarning in asLicenseWarnings:
                fOutput("@ %s" % sLicenseWarning)
            fOutput("@" * 80)
            gbLicenseWarningsShown = True

    def fLicenseErrorsCallback(oBugId, asLicenseErrors):
        fOutput("@" * 80)
        for sLicenseError in asLicenseErrors:
            fOutput("@ %s" % sLicenseError)
        fOutput("@" * 80)
        os._exit(1)

    def fInternalExceptionCallback(oBugId, oException, oTraceBack):
        global gbTestFailed
        gbTestFailed = True
        if not gbShowCdbIO:
            for sLine in asLog:
                fOutput(sLine)
        fOutput("@" * 80)
        fOutput("- An internal exception has occured in test: %s" %
                sTestDescription)
        fOutput("  %s" % repr(oException))
        fOutput("  Stack:")
        txStack = traceback.extract_tb(oTraceBack)
        uFrameIndex = len(txStack) - 1
        for (sFileName, uLineNumber, sFunctionName,
             sCode) in reversed(txStack):
            sSource = "%s/%d" % (sFileName, uLineNumber)
            if sFunctionName != "<module>":
                sSource = "%s (%s)" % (sFunctionName, sSource)
            fOutput("  %3d %s" % (uFrameIndex, sSource))
            if sCode:
                fOutput("      > %s" % sCode.strip())
            uFrameIndex -= 1
        fOutput("@" * 80)
        oBugId.fStop()

    def fPageHeapNotEnabledCallback(oBugId, oProcess, bIsMainProcess,
                                    bPreventable):
        assert oProcess.sBinaryName == "cmd.exe", \
            "It appears you have not enabled page heap for %s, which is required to run tests." % sBinaryName

    def fProcessAttachedCallback(oBugId, oProcess, bIsMainProcess):
        asLog.append("%s process %d/0x%X (%s): attached." % (bIsMainProcess and "Main" or "Sub", \
            oProcess.uId, oProcess.uId, oProcess.sBinaryName))

    def fProcessStartedCallback(oBugId, oConsoleProcess, bIsMainProcess):
        # This is always a main process
        asLog.append("%s process %d/0x%X (%s): started." % \
            (bIsMainProcess and "Main" or "Sub", oConsoleProcess.uId, oConsoleProcess.uId, oConsoleProcess.sBinaryName))

    def fProcessTerminatedCallback(oBugId, oProcess, bIsMainProcess):
        asLog.append("%s process %d/0x%X (%s): terminated." % (bIsMainProcess and "Main" or "Sub", \
            oProcess.uId, oProcess.uId, oProcess.sBinaryName))

    def fLogMessageCallback(oBugId, sMessage, dsData=None):
        sData = dsData and ", ".join(
            ["%s: %s" % (sName, sValue) for (sName, sValue) in dsData.items()])
        sLogLine = "log>%s%s" % (sMessage, sData and " (%s)" % sData or "")
        if gbShowCdbIO: fOutput(sLogLine)
        asLog.append(sLogLine)

    aoBugReports = []

    def fBugReportCallback(oBugId, oBugReport):
        aoBugReports.append(oBugReport)

    if gbShowCdbIO:
        fOutput()
        fOutput("=" * 80)
        fOutput("%s %s" %
                (sApplicationBinaryPath, " ".join(asApplicationArguments)))
        if asExpectedBugIdAndLocations:
            for sExpectedBugIdAndLocation in asExpectedBugIdAndLocations:
                fOutput("  => %s" % sExpectedBugIdAndLocation)
        fOutput("-" * 80)
    bBugIdStarted = False
    bBugIdStopped = False
    try:
        oBugId = cBugId(
            sCdbISA=
            sISA,  # Debug with a cdb.exe for an ISA that matches the target process.
            sApplicationBinaryPath=sApplicationBinaryPath,
            asApplicationArguments=asApplicationArguments,
            asSymbolServerURLs=["http://msdl.microsoft.com/download/symbols"
                                ],  # Will be ignore if symbols are disabled.
            bGenerateReportHTML=gbGenerateReportHTML,
            uTotalMaxMemoryUse=guTotalMaxMemoryUse,
            uMaximumNumberOfBugs=uMaximumNumberOfBugs,
        )
        oBugId.fAddEventCallback("Application resumed",
                                 fApplicationResumedCallback)
        oBugId.fAddEventCallback("Application running",
                                 fApplicationRunningCallback)
        oBugId.fAddEventCallback("Application debug output",
                                 fApplicationDebugOutputCallback)
        oBugId.fAddEventCallback("Application stderr output",
                                 fApplicationStdErrOutputCallback)
        oBugId.fAddEventCallback("Application stdout output",
                                 fApplicationStdOutOutputCallback)
        oBugId.fAddEventCallback("Application suspended",
                                 fApplicationSuspendedCallback)
        oBugId.fAddEventCallback("Bug report", fBugReportCallback)
        oBugId.fAddEventCallback("Cdb stderr output", fCdbStdErrOutputCallback)
        oBugId.fAddEventCallback("Cdb stdin input", fCdbStdInInputCallback)
        oBugId.fAddEventCallback("Cdb stdout output", fCdbStdOutOutputCallback)
        oBugId.fAddEventCallback("Failed to apply application memory limits",
                                 fFailedToApplyMemoryLimitsCallback)
        oBugId.fAddEventCallback("Failed to apply process memory limits",
                                 fFailedToApplyMemoryLimitsCallback)
        oBugId.fAddEventCallback("Failed to debug application",
                                 fFailedToDebugApplicationCallback)
        oBugId.fAddEventCallback("Finished", fFinishedCallback)
        oBugId.fAddEventCallback("Internal exception",
                                 fInternalExceptionCallback)
        oBugId.fAddEventCallback("License warnings", fLicenseWarningsCallback)
        oBugId.fAddEventCallback("License errors", fLicenseErrorsCallback)
        oBugId.fAddEventCallback("Page heap not enabled",
                                 fPageHeapNotEnabledCallback)
        oBugId.fAddEventCallback("Process attached", fProcessAttachedCallback)
        oBugId.fAddEventCallback("Process terminated",
                                 fProcessTerminatedCallback)
        oBugId.fAddEventCallback("Process started", fProcessStartedCallback)
        oBugId.fAddEventCallback("Log message", fLogMessageCallback)
        if bExcessiveCPUUsageChecks:

            def fExcessiveCPUUsageDetectedCallback(oBugId,
                                                   bExcessiveCPUUsageDetected):
                if not bExcessiveCPUUsageDetected:
                    oBugId.fCheckForExcessiveCPUUsage(
                        fExcessiveCPUUsageDetectedCallback)

            oBugId.foSetTimeout(
                sDescription="Start check for excessive CPU usage",
                nTimeout=cBugId.
                dxConfig["nExcessiveCPUUsageCheckInitialTimeout"],
                fCallback=lambda oBugId: fExcessiveCPUUsageDetectedCallback(
                    oBugId, False),
            )
        oBugId.fStart()
        bBugIdStarted = True
        oBugId.fWait()
        bBugIdStopped = True
        if gbShowCdbIO: fOutput("= Finished ".ljust(80, "="))
        if gbTestFailed:
            return

        def fDumpExpectedAndReported():
            uCounter = 0
            while 1:
                sExpectedBugIdAndLocation = uCounter < len(
                    asExpectedBugIdAndLocations
                ) and asExpectedBugIdAndLocations[uCounter]
                oBugReport = uCounter < len(
                    aoBugReports) and aoBugReports[uCounter]
                if not sExpectedBugIdAndLocation and not oBugReport:
                    break
                uCounter += 1
                fOutput("  Expected #%d: %s" %
                        (uCounter, sExpectedBugIdAndLocation))
                fOutput("  Reported   : %s" %
                        (oBugReport and "%s @ %s" %
                         (oBugReport.sId, oBugReport.sBugLocation)))
                if oBugReport:
                    fOutput("               %s" % oBugReport.sBugDescription)

        if sExpectedFailedToDebugApplicationErrorMessage:
            pass
        elif asExpectedBugIdAndLocations is None:
            uCounter = 0
            for oBugReport in aoBugReports:
                uCounter += 1
                sBugIdAndLocation = "%s @ %s" % (oBugReport.sId,
                                                 oBugReport.sBugLocation)
                fOutput("* Test result for: %s" % sTestDescription)
                fOutput("  Test bug #%d: %s." % (uCounter, sBugIdAndLocation))
        elif asExpectedBugIdAndLocations:
            if len(aoBugReports) != len(asExpectedBugIdAndLocations):
                gbTestFailed = True
                if not gbShowCdbIO:
                    for sLine in asLog:
                        fOutput(sLine)
                fOutput("- Failed test: %s" % sTestDescription)
                fOutput(
                    "  Test reported %d instead of %d bugs in the application."
                    % (len(aoBugReports), len(asExpectedBugIdAndLocations)))
                fDumpExpectedAndReported()
            else:
                uCounter = 0
                for oBugReport in aoBugReports:
                    sExpectedBugIdAndLocation = asExpectedBugIdAndLocations[
                        uCounter]
                    uCounter += 1
                    sBugIdAndLocation = "%s @ %s" % (oBugReport.sId,
                                                     oBugReport.sBugLocation)
                    if sExpectedBugIdAndLocation[
                            0] == "*":  # string contains a regular expression
                        # Remove "*" and insert (escaped) test binary name in location
                        sExpectedBugIdAndLocation = "^(%s)$" % sExpectedBugIdAndLocation[
                            1:].replace("<test-binary>",
                                        re.escape(sTestBinaryName))
                        bSuccess = re.match(sExpectedBugIdAndLocation,
                                            sBugIdAndLocation)
                    else:
                        sExpectedBugIdAndLocation = sExpectedBugIdAndLocation.replace(
                            "<test-binary>", sTestBinaryName)
                        bSuccess = sBugIdAndLocation == sExpectedBugIdAndLocation
                    if not bSuccess:
                        gbTestFailed = True
                        if not gbShowCdbIO:
                            for sLine in asLog:
                                fOutput(sLine)
                        fOutput("- Failed test: %s" % sTestDescription)
                        fOutput("  Test bug #%d does not match %s." %
                                (uCounter, sExpectedBugIdAndLocation))
                        fDumpExpectedAndReported()
                        break
        if gbSaveReportHTML:
            for oBugReport in aoBugReports:
                # We'd like a report file name base on the BugId, but the later may contain characters that are not valid in a file name
                sDesiredReportFileName = "%s %s @ %s.html" % (
                    sPythonISA, oBugReport.sId, oBugReport.sBugLocation)
                # Thus, we need to translate these characters to create a valid filename that looks very similar to the BugId
                sValidReportFileName = mFileSystem.fsValidName(
                    sDesiredReportFileName, bUnicode=False)
                sReportsFilePath = mFileSystem.fsPath(sReportsFolderName,
                                                      sValidReportFileName)
                mFileSystem.fWriteDataToFile(
                    oBugReport.sReportHTML,
                    sReportsFilePath,
                    fbRetryOnFailure=lambda: False,
                )
                fOutput("  Wrote report: %s" % sReportsFilePath)
    except Exception, oException:
        if bBugIdStarted and not bBugIdStopped:
            oBugId.fTerminate()
        fOutput("- Failed test: %s" % sTestDescription)
        fOutput("  Exception:   %s" % repr(oException))
        raise
Ejemplo n.º 4
0
# CPU usage should normalize after half a second.
cBugId.dxConfig["nExcessiveCPUUsageCheckInterval"] = 0.5
# Excessive CPU usage should be apparent within half a second.
cBugId.dxConfig["uArchitectureIndependentBugIdBits"] = 32
# Test architecture independent bug ids

sPythonISA = {
    "32bit": "x86",
    "64bit": "x64",
}[platform.architecture()[0]]
asTestISAs = {
    "x86": ["x86"],
    "x64": ["x64", "x86"],
}[sPythonISA]

sReportsFolderName = mFileSystem.fsPath(sTestsFolderPath, "Reports")

dsBinaries_by_sISA = {
    "x86": os.path.join(sTestsFolderPath, "bin", "Tests_x86.exe"),
    "x64": os.path.join(sTestsFolderPath, "bin", "Tests_x64.exe"),
}

bFailed = False
gbGenerateReportHTML = False
gbSaveReportHTML = False
oOutputLock = threading.Lock()
guTotalMaxMemoryUse = 0x01234567
# The test application memory use limit: it should be large enough to allow the test
# to function, but small enough to detect excessive memory use before the entire
# system runs low on memory.
guOOMAllocationBlockSize = 0x1234
Ejemplo n.º 5
0
def fMain(asArguments):
  global \
      gasAttachToProcessesForExecutableNames, \
      gasBinaryNamesThatAreAllowedToRunWithoutPageHeap, \
      gbQuiet, \
      gbVerbose, \
      guDetectedBugsCount, \
      guMaximumNumberOfBugs;
  if len(asArguments) == 0:
    fPrintLogo();
    fPrintUsage(ddxApplicationSettings_by_sKeyword.keys());
    os._exit(0);
  # Parse all arguments until we encounter "--".
  sApplicationKeyword = None;
  sApplicationBinaryPath = None;
  auApplicationProcessIds = [];
  sUWPApplicationPackageName = None;
  sUWPApplicationId = None;
  asApplicationOptionalArguments = None;
  sApplicationISA = None;
  bRepeat = False;
  bCheckForUpdates = False;
  dxUserProvidedConfigSettings = {};
  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.");
        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();
      fPrintUsage(ddxApplicationSettings_by_sKeyword.keys());
      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.");
          os._exit(2);
        if sApplicationBinaryPath is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an application binary and process ids.");
          os._exit(2);
        if sUWPApplicationPackageName is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an UWP application package name and process ids.");
          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 an UWP application package name.");
          os._exit(2);
        if sUWPApplicationPackageName is not None:
          oConsole.fPrint(ERROR, "- You cannot provide multiple UWP application package names.");
          os._exit(2);
        if sApplicationBinaryPath is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an application binary and UWP package name.");
          os._exit(2);
        if len(auApplicationProcessIds) > 0:
          oConsole.fPrint(ERROR, "- You cannot provide process ids and an UWP application package name.");
          os._exit(2);
        if "!" not in sValue:
          oConsole.fPrint(ERROR, "- Please provide a string of the form ", ERROR_INFO, sSettingName, \
              "=<package name>!<application id>.");
          os._exit(2);
        sUWPApplicationPackageName, sUWPApplicationId = sValue.split("!", 1);
      elif sSettingName in ["help"]:
        fPrintLogo();
        fPrintUsage(ddxApplicationSettings_by_sKeyword.keys());
        os._exit(0);
      elif sSettingName in ["version", "check-for-updates"]:
        fVersionCheck();
        os._exit(0);
      elif sSettingName in ["isa", "cpu"]:
        if not sValue:
          oConsole.fPrint(ERROR, "- You must provide an Instruction Set Architecture.");
          os._exit(2);
        if sValue not in ["x86", "x64"]:
          oConsole.fPrint(ERROR, "- Unknown Instruction Set Architecture ", repr(sValue));
          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 ["repeat", "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 ["collateral"]:
        if sValue is None:
          guMaximumNumberOfBugs = guDefaultCollateralMaximumNumberOfBugs;
        else:
          guMaximumNumberOfBugs = long(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.");
          os._exit(2);
        try:
          xValue = json.loads(sValue);
        except ValueError as oError:
          oConsole.fPrint(ERROR, "- Cannot decode argument JSON value ", ERROR_INFO, "--", sSettingName, "=", sValue, \
              ERROR, ": ", ERROR_INFO, " ".join(oError.args), ERROR, ".");
          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.");
        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, ".");
        os._exit(2);
      fPrintApplicationKeyWordHelp(sApplicationKeyword, dxApplicationSettings);
      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();
        os._exit(2);
      if len(auApplicationProcessIds) > 0:
        oConsole.fPrint(ERROR, "- You cannot provide process ids and an application binary.");
        os._exit(2);
      if sUWPApplicationPackageName is not None:
        oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name and a binary.");
        os._exit(2);
      sApplicationBinaryPath = sArgument;
  
  if bFast:
    gbQuiet = True;
    dxUserProvidedConfigSettings["bGenerateReportHTML"] = False;
    dxUserProvidedConfigSettings["asSymbolServerURLs"] = [];
    dxUserProvidedConfigSettings["cBugId.bUse_NT_SYMBOL_PATH"] = False;
  
  dsApplicationURLTemplate_by_srSourceFilePath = {};
  
  fCleanup = None;
  if sApplicationKeyword:
    dxApplicationSettings = ddxApplicationSettings_by_sKeyword.get(sApplicationKeyword);
    if not dxApplicationSettings:
      oConsole.fPrint(ERROR, "- Unknown application keyword ", ERROR_INFO, sApplicationKeyword, ERROR, ".");
      os._exit(2);
    fCheckApplication = dxApplicationSettings.get("fCheckApplication");
    if fCheckApplication:
      fCheckApplication(); # This should terminate BugId with the relevant exit code if needed.
    fCleanup = 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, ".");
        os._exit(2);
      if sUWPApplicationPackageName:
        oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name for application keyword ", \
            ERROR_INFO, sApplicationKeyword, ERROR, ".");
        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.");
          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, ".");
        os._exit(2);
      if auApplicationProcessIds:
        oConsole.fPrint(ERROR, "- You cannot provide process ids for application keyword ", ERROR_INFO, \
            sApplicationKeyword, ERROR, ".");
        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, ".");
      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, ".");
      os._exit(2);
    if "asApplicationAttachToProcessesForExecutableNames" in dxApplicationSettings:
      gasAttachToProcessesForExecutableNames = dxApplicationSettings["asApplicationAttachToProcessesForExecutableNames"];
    # 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:
          fApplyConfigSetting(sSettingName, xValue, [None, "  "][gbVerbose]); # Apply and show result indented.
      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();
    os._exit(2);
  
  # Apply user provided settings:
  for (sSettingName, xValue) in dxUserProvidedConfigSettings.items():
    fApplyConfigSetting(sSettingName, xValue, [None, ""][gbVerbose]); # Apply and show result
  
  if bRepeat:
    duNumberOfRepros_by_sBugIdAndLocation = {};
    sValidStatisticsFileName = mFileSystem.fsValidName("Reproduction statistics.txt");
  uRunCounter = 0;
  while 1: # Will only loop if bRepeat is True
    nStartTime = time.clock();
    if fCleanup and dxConfig["bCleanup"]:
      oConsole.fStatus("* Cleaning up application state...");
      fCleanup();
    uRunCounter += 1;
    oConsole.fLock();
    try:
      if 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();
    oBugId = cBugId(
      sCdbISA = sApplicationISA or cBugId.sOSISA,
      sApplicationBinaryPath = sApplicationBinaryPath or None,
      auApplicationProcessIds = auApplicationProcessIds or None,
      sUWPApplicationPackageName = sUWPApplicationPackageName or None,
      sUWPApplicationId = sUWPApplicationId or None,
      asApplicationArguments = asApplicationArguments,
      asLocalSymbolPaths = dxConfig["asLocalSymbolPaths"],
      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("Failed to debug application", fFailedToDebugApplicationCallback);
    oBugId.fAddEventCallback("Failed to apply application memory limits", fFailedToApplyApplicationMemoryLimitsCallback);
    oBugId.fAddEventCallback("Failed to apply process memory limits", fFailedToApplyProcessMemoryLimitsCallback);
    oBugId.fAddEventCallback("Application running", fApplicationRunningCallback);
    oBugId.fAddEventCallback("Application suspended", fApplicationSuspendedCallback);
    oBugId.fAddEventCallback("Application resumed", fApplicationResumedCallback);
    oBugId.fAddEventCallback("Process terminated", fProcessTerminatedCallback);
    oBugId.fAddEventCallback("Internal exception", fInternalExceptionCallback);
    oBugId.fAddEventCallback("Page heap not enabled", fPageHeapNotEnabledCallback);
    if gbVerbose:
      oBugId.fAddEventCallback("Cdb stdin input", fCdbStdInInputCallback);
      oBugId.fAddEventCallback("Cdb stdout output", fCdbStdOutOutputCallback);
    oBugId.fAddEventCallback("Cdb stderr output", fCdbStdErrOutputCallback);
    oBugId.fAddEventCallback("Started process", fStartedProcessCallback);
    oBugId.fAddEventCallback("Attached to process", fAttachedToProcessCallback);
    oBugId.fAddEventCallback("Application stdout output", fApplicationStdOutOutputCallback);
    oBugId.fAddEventCallback("Application stderr output", fApplicationStdOutOutputCallback);
    oBugId.fAddEventCallback("Bug report", fBugReportCallback);

    if dxConfig["nApplicationMaxRunTime"] is not None:
      oBugId.foSetTimeout("Maximum application runtime", dxConfig["nApplicationMaxRunTime"], \
          fApplicationMaxRunTimeCallback);
    if dxConfig["bExcessiveCPUUsageCheckEnabled"] and dxConfig["nExcessiveCPUUsageCheckInitialTimeout"]:
      oBugId.fSetCheckForExcessiveCPUUsageTimeout(dxConfig["nExcessiveCPUUsageCheckInitialTimeout"]);
    guDetectedBugsCount = 0;
    oBugId.fStart();
    oBugId.fWait();
    if gbAnErrorOccured:
      os._exit(3);
    if guDetectedBugsCount == 0:
      oConsole.fPrint(u"\u2500\u2500 The application terminated without a bug being detected ", sPadding = u"\u2500");
      sBugIdAndLocation = "No crash";
    if gbVerbose:
      oConsole.fPrint("  Application time: %s seconds" % (long(oBugId.fnApplicationRunTime() * 1000) / 1000.0));
      nOverheadTime = time.clock() - nStartTime - oBugId.fnApplicationRunTime();
      oConsole.fPrint("  BugId overhead:   %s seconds" % (long(nOverheadTime * 1000) / 1000.0));
    if not bRepeat:
      os._exit(guDetectedBugsCount > 0 and 1 or 0);
    duNumberOfRepros_by_sBugIdAndLocation.setdefault(sBugIdAndLocation, 0)
    duNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] += 1;
    sStatistics = "";
    auOrderedNumberOfRepros = sorted(list(set(duNumberOfRepros_by_sBugIdAndLocation.values())));
    auOrderedNumberOfRepros.reverse();
    for uNumberOfRepros in auOrderedNumberOfRepros:
      for sBugIdAndLocation in duNumberOfRepros_by_sBugIdAndLocation.keys():
        if duNumberOfRepros_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 = mFileSystem.fsPath(dxConfig["sReportFolderPath"], sValidStatisticsFileName);
    else:
      sStatisticsFilePath = mFileSystem.fsPath(sValidStatisticsFileName);
    eWriteDataToFileResult = mFileSystem.feWriteDataToFile(
      sStatistics,
      sStatisticsFilePath,
      fbRetryOnFailure = lambda: False,
    );
    if eWriteDataToFileResult:
      oConsole.fPrint("  Statistics:       ", ERROR, "Cannot be saved (", ERROR_INFO, repr(eWriteDataToFileResult), \
          ERROR, ")");
    else:
      oConsole.fPrint("  Statistics:       ", INFO, sStatisticsFilePath, NORMAL, " (%d bytes)" % len(sStatistics));
    oConsole.fPrint(); # and loop
Ejemplo n.º 6
0
from dxConfig import dxConfig
from fsFirstExistingFile import fsFirstExistingFile
from mColors import *
from mWindowsAPI import fauProcessesIdsForExecutableNames, fbTerminateProcessForId, oSystemInfo
from oConsole import oConsole
import mFileSystem

sLocalAppData = os.getenv("LocalAppData")

dxConfigSettings = {
    "bApplicationTerminatesWithMainProcess": True,
    "cBugId.bIgnoreCPPExceptions": True,
    "cBugId.bIgnoreWinRTExceptions": True,
}

sEdgeRecoveryPath = mFileSystem.fsPath(os.getenv("LocalAppData"), \
    "Packages", "Microsoft.MicrosoftEdge_8wekyb3d8bbwe", "AC", "MicrosoftEdge", "User", "Default", "Recovery", "Active")


def fEdgeSetup(bFirstRun):
    if bFirstRun:
        if oSystemInfo.uOSBuild < 15063:
            oConsole.fPrint(
                ERROR,
                "Debugging Microsoft Edge directly using BugId is only supported on Windows"
            )
            oConsole.fPrint(ERROR, "builds ", ERROR_INFO, "15063", ERROR, " and higher, and you are running build ", \
                ERROR_INFO, oSystemInfo.sOSBuild, ERROR, ".")
            oConsole.fPrint()
            oConsole.fPrint("You could try using the ", INFO, "EdgeBugId.cmd",
                            NORMAL, " script that comes with EdgeDbg,")
            oConsole.fPrint("which you can download from ", INFO,
Ejemplo n.º 7
0
import codecs, os, sys
sys.stdout = codecs.getwriter("cp437")(sys.stdout, "replace")

sModuleFolderPath = os.path.dirname(os.path.abspath(__file__))
sBaseFolderPath = os.path.dirname(sModuleFolderPath)
sys.path.extend([
    sBaseFolderPath,
    sModuleFolderPath,
    os.path.join(sModuleFolderPath, "modules"),
])

import mFileSystem

sTempFolderPath = mFileSystem.fsPath(os.environ["TEMP"])
sSpecialChars = 'test[[[\0\r\n"<>\\/?*:|]]]'

sSpecialCharsPath = mFileSystem.fsPath(sTempFolderPath, sSpecialChars)

sCWD = os.getcwdu().rstrip("\\")
sCWDDrive, sCWDPath = os.path.splitdrive(sCWD)
print "* Testing fsPath..."
dsPathTests = {
    r".": r"\\?\%s" % sCWD,
    r"x": r"\\?\%s\x" % sCWD,
    r"x\y": r"\\?\%s\x\y" % sCWD,
    r"%sx\y" % sCWDDrive: r"\\?\%s\x\y" % sCWD,
    r"%s\x\y" % sCWDDrive: r"\\?\%s\x\y" % sCWDDrive,
    r"x:\y\z": r"\\?\x:\y\z",
    r"\\?\x:\y\z": r"\\?\x:\y\z",
    r"\\x\y\z": r"\\?\UNC\x\y\z",
    r"\\?\UNC\x\y\z": r"\\?\UNC\x\y\z",
Ejemplo n.º 8
0
def fuMain(asArguments):
  global gbVerbose, gbQuiet, gasAttachToProcessesForExecutableNames;
  if len(asArguments) == 0:
    fPrintLogo();
    fPrintUsage(asApplicationKeywords);
    return 0;
  # Parse all arguments until we encounter "--".
  sApplicationKeyword = None;
  sApplicationBinaryPath = None;
  auApplicationProcessIds = [];
  sUWPApplicationPackageName = None;
  sUWPApplicationId = None;
  asApplicationOptionalArguments = None;
  sApplicationISA = None;
  bRepeat = False;
  bCheckForUpdates = False;
  dxUserProvidedConfigSettings = {};
  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.");
        return 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 ["-?", "/?", "-h", "/h"]:
      fPrintLogo();
      fPrintUsage(asApplicationKeywords);
      return 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.");
          return 2;
        if sApplicationBinaryPath is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an application binary and process ids.");
          return 2;
        if sUWPApplicationPackageName is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an UWP application package name and process ids.");
          return 2;
        auApplicationProcessIds += [long(x) for x in sValue.split(",")];
      elif sSettingName in ["uwp", "uwp-app"]:
        if not sValue:
          oConsole.fPrint(ERROR, "- You must provide an UWP application package name.");
          return 2;
        if sUWPApplicationPackageName is not None:
          oConsole.fPrint(ERROR, "- You cannot provide multiple UWP application package names.");
          return 2;
        if sApplicationBinaryPath is not None:
          oConsole.fPrint(ERROR, "- You cannot provide an application binary and UWP package name.");
          return 2;
        if len(auApplicationProcessIds) > 0:
          oConsole.fPrint(ERROR, "- You cannot provide process ids and an UWP application package name.");
          return 2;
        if "!" not in sValue:
          oConsole.fPrint(ERROR, "- Please provide a string of the form ", ERROR_INFO, sSettingName, "=<package name>!<application id>.");
          return 2;
        sUWPApplicationPackageName, sUWPApplicationId = sValue.split("!", 1);
      elif sSettingName in ["version", "check-for-updates"]:
        fVersionCheck();
        return 0;
      elif sSettingName in ["isa", "cpu"]:
        if not sValue:
          oConsole.fPrint(ERROR, "- You must provide an Instruction Set Architecture.");
          return 2;
        if sValue not in ["x86", "x64"]:
          oConsole.fPrint(ERROR, "- Unknown Instruction Set Architecture ", repr(sValue));
          return 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 ["repeat", "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 ["test-internal-error", "internal-error-test"]:
        raise Exception("Testing internal error");
      else:
        try:
          xValue = json.loads(sValue);
        except ValueError as oError:
          oConsole.fPrint(ERROR, "- Cannot decode argument JSON value ", ERROR_INFO, sValue, ERROR, ": ", \
              ERROR_INFO, " ".join(oError.args), ERROR, ".");
          return 2;
        # User provided config settings must be applied after any keyword specific config settings:
        dxUserProvidedConfigSettings[sSettingName] = xValue;
    elif sArgument in asApplicationKeywords:
      if sApplicationKeyword is not None:
        oConsole.fPrint(ERROR, "- You cannot provide multiple application keywords.");
        return 2;
      sApplicationKeyword = sArgument;
    elif sArgument[-1] == "?" and sArgument[:-1] in asApplicationKeywords:
      return fuShowApplicationKeyWordHelp(sArgument[:-1]);
    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();
        return 2;
      if len(auApplicationProcessIds) > 0:
        oConsole.fPrint(ERROR, "- You cannot provide process ids and an application binary.");
        return 2;
      if sUWPApplicationPackageName is not None:
        oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name and a binary.");
        return 2;
      sApplicationBinaryPath = sArgument;
  
  if bFast:
    gbQuiet = True;
    dxUserProvidedConfigSettings["bGenerateReportHTML"] = False;
    dxUserProvidedConfigSettings["asSymbolServerURLs"] = [];
    dxUserProvidedConfigSettings["cBugId.bUse_NT_SYMBOL_PATH"] = False;
  
  dsURLTemplate_by_srSourceFilePath = {};
  rImportantStdOutLines = None;
  rImportantStdErrLines = None;
  
  if sApplicationKeyword:
    fCleanup = dxConfig["bCleanup"] and gdfCleanup_by_sKeyword.get(sApplicationKeyword) or None;
    # Get application binary/UWP package name/process ids as needed:
    if sApplicationKeyword in gdApplication_sBinaryPath_by_sKeyword:
      # 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, ".");
        return 2;
      if sUWPApplicationPackageName:
        oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name for application keyword ", \
            ERROR_INFO, sApplicationKeyword, ERROR, ".");
        return 2;
      if sApplicationBinaryPath is None:
        sApplicationBinaryPath = gdApplication_sBinaryPath_by_sKeyword[sApplicationKeyword];
        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.");
          return 4;
    elif sApplicationKeyword in gsUWPApplicationPackageName_by_sKeyword:
      # This application is started as an application package.
      if sApplicationBinaryPath:
        oConsole.fPrint(ERROR, "- You cannot provide an application binary for application keyword ", \
            ERROR_INFO, sApplicationKeyword, ERROR, ".");
        return 2;
      sUWPApplicationPackageName = gsUWPApplicationPackageName_by_sKeyword[sApplicationKeyword];
      sUWPApplicationId = gsUWPApplicationId_by_sKeyword[sApplicationKeyword];
    elif not auApplicationProcessIds:
      # This application is attached to.
      oConsole.fPrint(ERROR, "- You must provide process ids for application keyword ", \
          ERROR_INFO, sApplicationKeyword, ERROR, ".");
      return 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, ".");
      return 2;
    if sApplicationKeyword in gasApplicationAttachToProcessesForExecutableNames_by_sKeyword:
      gasAttachToProcessesForExecutableNames = gasApplicationAttachToProcessesForExecutableNames_by_sKeyword[sApplicationKeyword];
    # Get application arguments;
    fasGetApplicationStaticArguments = gdApplication_fasGetStaticArguments_by_sKeyword.get(sApplicationKeyword, None);
    asApplicationStaticArguments = fasGetApplicationStaticArguments and fasGetApplicationStaticArguments(bForHelp = False) or [];
    if asApplicationOptionalArguments is None:
      asApplicationOptionalArguments = [
        sArgument is DEFAULT_BROWSER_TEST_URL and dxConfig["sDefaultBrowserTestURL"] or sArgument
        for sArgument in gdApplication_asDefaultOptionalArguments_by_sKeyword.get(sApplicationKeyword, [])
      ];
    asApplicationArguments = asApplicationStaticArguments + asApplicationOptionalArguments;
    # Apply application specific settings
    if sApplicationKeyword in gdApplication_dxSettings_by_sKeyword:
      if gbVerbose:
        oConsole.fPrint("* Applying application specific configuration for %s:" % sApplicationKeyword);
      for (sSettingName, xValue) in gdApplication_dxSettings_by_sKeyword[sApplicationKeyword].items():
        if sSettingName not in dxUserProvidedConfigSettings:
          fApplyConfigSetting(sSettingName, xValue, "  "); # Apply and show result indented.
      if gbVerbose:
        oConsole.fPrint();
    # Apply application specific source settings
    if sApplicationKeyword in gdApplication_sURLTemplate_by_srSourceFilePath_by_sKeyword:
      dsURLTemplate_by_srSourceFilePath = gdApplication_sURLTemplate_by_srSourceFilePath_by_sKeyword[sApplicationKeyword];
    # Apply application specific stdio settings:
    if sApplicationKeyword in gdApplication_rImportantStdOutLines_by_sKeyword:
      rImportantStdOutLines = gdApplication_rImportantStdOutLines_by_sKeyword[sApplicationKeyword];
    if sApplicationKeyword in gdApplication_rImportantStdErrLines_by_sKeyword:
      rImportantStdErrLines = gdApplication_rImportantStdErrLines_by_sKeyword[sApplicationKeyword];
    if not sApplicationISA and sApplicationKeyword in gdApplication_sISA_by_sKeyword:
      # Apply application specific ISA
      sApplicationISA = gdApplication_sISA_by_sKeyword[sApplicationKeyword];
  elif (auApplicationProcessIds or sUWPApplicationPackageName or sApplicationBinaryPath):
    fCleanup = None;
    # 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();
    return 2;
  
  # Apply user provided settings:
  for (sSettingName, xValue) in dxUserProvidedConfigSettings.items():
    fApplyConfigSetting(sSettingName, xValue, ""); # Apply and show result
  
  if bRepeat:
    duNumberOfRepros_by_sBugIdAndLocation = {};
    sValidStatisticsFileName = mFileSystem.fsValidName("Reproduction statistics.txt");
  uRunCounter = 0;
  while 1: # Will only loop if bRepeat is True
    nStartTime = time.clock();
    if fCleanup is not None:
      oConsole.fStatus("* Cleaning up application state...");
      fCleanup();
    uRunCounter += 1;
    oConsole.fLock();
    try:
      if 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();
    oBugId = cBugId(
      sCdbISA = sApplicationISA or cBugId.sOSISA,
      sApplicationBinaryPath = sApplicationBinaryPath or None,
      auApplicationProcessIds = auApplicationProcessIds or None,
      sUWPApplicationPackageName = sUWPApplicationPackageName or None,
      sUWPApplicationId = sUWPApplicationId or None,
      asApplicationArguments = asApplicationArguments,
      asLocalSymbolPaths = dxConfig["asLocalSymbolPaths"],
      asSymbolCachePaths = dxConfig["asSymbolCachePaths"], 
      asSymbolServerURLs = dxConfig["asSymbolServerURLs"],
      dsURLTemplate_by_srSourceFilePath = dsURLTemplate_by_srSourceFilePath,
      rImportantStdOutLines = rImportantStdOutLines,
      rImportantStdErrLines = rImportantStdErrLines,
      bGenerateReportHTML = dxConfig["bGenerateReportHTML"],
      uProcessMaxMemoryUse = dxConfig["uProcessMaxMemoryUse"],
      uTotalMaxMemoryUse = dxConfig["uTotalMaxMemoryUse"],
      fFailedToDebugApplicationCallback = fFailedToDebugApplicationHandler,
      fFailedToApplyMemoryLimitsCallback = fFailedToApplyMemoryLimitsHandler,
      fApplicationRunningCallback = fApplicationRunningHandler,
      fApplicationSuspendedCallback = fApplicationSuspendedHandler,
      fApplicationResumedCallback = fApplicationResumedHandler,
      fMainProcessTerminatedCallback = fMainProcessTerminatedHandler,
      fInternalExceptionCallback = fInternalExceptionHandler,
      fFinishedCallback = None,
      fPageHeapNotEnabledCallback = fPageHeapNotEnabledHandler,
      fStdInInputCallback = gbVerbose and fStdInInputHandler or None,
      fStdOutOutputCallback = gbVerbose and fStdOutOutputHandler or None,
      fStdErrOutputCallback = fStdErrOutputHandler,
      fNewProcessCallback = fNewProcessHandler,
      fApplicationStdOutOrErrOutputCallback = fApplicationStdOutOrErrOutputHandler,
    );
    if dxConfig["nApplicationMaxRunTime"] is not None:
      oBugId.foSetTimeout("Maximum application runtime", dxConfig["nApplicationMaxRunTime"], fApplicationRunTimeHandler);
    if dxConfig["bExcessiveCPUUsageCheckEnabled"] and dxConfig["nExcessiveCPUUsageCheckInitialTimeout"]:
      oBugId.fSetCheckForExcessiveCPUUsageTimeout(dxConfig["nExcessiveCPUUsageCheckInitialTimeout"]);
    oBugId.fStart();
    oBugId.fWait();
    if gbAnErrorOccured:
      return 3;
    oConsole.fLock();
    try:
      if oBugId.oBugReport is not None:
        oConsole.fPrint(HILITE, "+ A bug was detect in the application:");
        if oBugId.oBugReport.sBugLocation:
          oConsole.fPrint("  Id @ Location:    ", INFO, oBugId.oBugReport.sId, NORMAL, " @ ", INFO, oBugId.oBugReport.sBugLocation);
          sBugIdAndLocation = "%s @ %s" % (oBugId.oBugReport.sId, oBugId.oBugReport.sBugLocation);
        else:
          oConsole.fPrint("  Id:               ", INFO, oBugId.oBugReport.sId);
          sBugIdAndLocation = oBugId.oBugReport.sId;
        if oBugId.oBugReport.sBugSourceLocation:
          oConsole.fPrint("  Source:           ", INFO, oBugId.oBugReport.sBugSourceLocation);
        oConsole.fPrint("  Description:      ", INFO, oBugId.oBugReport.sBugDescription);
        oConsole.fPrint("  Security impact:  ", INFO, oBugId.oBugReport.sSecurityImpact);
        oConsole.fPrint("  Version:          ", HILITE, oBugId.oBugReport.asVersionInformation[0]); # There is always the process' binary.
        for sVersionInformation in oBugId.oBugReport.asVersionInformation[1:]: # There may be two if the crash was in a
          oConsole.fPrint("                    ", HILITE, sVersionInformation); # different binary (e.g. a .dll)
        if dxConfig["bGenerateReportHTML"]:
          # We'd like a report file name base on the BugId, but the later may contain characters that are not valid in a file name
          sDesiredReportFileName = "%s.html" % sBugIdAndLocation;
          # Thus, we need to translate these characters to create a valid filename that looks very similar to the BugId
          sValidReportFileName = mFileSystem.fsValidName(sDesiredReportFileName, bUnicode = dxConfig["bUseUnicodeReportFileNames"]);
          if dxConfig["sReportFolderPath"] is not None:
            sReportFilePath = mFileSystem.fsPath(dxConfig["sReportFolderPath"], sValidReportFileName);
          else:
            sReportFilePath = mFileSystem.fsPath(sValidReportFileName);
          eWriteDataToFileResult = mFileSystem.feWriteDataToFile(
            oBugId.oBugReport.sReportHTML,
            sReportFilePath,
            fbRetryOnFailure = lambda: False,
          );
          if eWriteDataToFileResult:
            oConsole.fPrint("  Bug report:       ", ERROR, "Cannot be saved (", \
                ERROR_INFO, repr(eWriteDataToFileResult), ERROR, ")");
          else:
            oConsole.fPrint("  Bug report:       ", HILITE, sValidReportFileName, NORMAL, " (%d bytes)" % len(oBugId.oBugReport.sReportHTML));
      else:
        oConsole.fPrint("+ The application terminated without a bug being detected.");
        sBugIdAndLocation = "No crash";
      if gbVerbose:
        oConsole.fPrint("  Application time: %s seconds" % (long(oBugId.fnApplicationRunTime() * 1000) / 1000.0));
        nOverheadTime = time.clock() - nStartTime - oBugId.fnApplicationRunTime();
        oConsole.fPrint("  BugId overhead:   %s seconds" % (long(nOverheadTime * 1000) / 1000.0));
      if not bRepeat: return oBugId.oBugReport is not None and 1 or 0;
      duNumberOfRepros_by_sBugIdAndLocation.setdefault(sBugIdAndLocation, 0)
      duNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] += 1;
      sStatistics = "";
      auOrderedNumberOfRepros = sorted(list(set(duNumberOfRepros_by_sBugIdAndLocation.values())));
      auOrderedNumberOfRepros.reverse();
      for uNumberOfRepros in auOrderedNumberOfRepros:
        for sBugIdAndLocation in duNumberOfRepros_by_sBugIdAndLocation.keys():
          if duNumberOfRepros_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 = mFileSystem.fsPath(dxConfig["sReportFolderPath"], sValidStatisticsFileName);
      else:
        sStatisticsFilePath = mFileSystem.fsPath(sValidStatisticsFileName);
      eWriteDataToFileResult = mFileSystem.feWriteDataToFile(
        sStatistics,
        sStatisticsFilePath,
        fbRetryOnFailure = lambda: False,
      );
      if eWriteDataToFileResult:
        oConsole.fPrint("  Statistics:       ", ERROR, "Cannot be saved (", ERROR_INFO, repr(eWriteDataToFileResult), ERROR, ")");
      else:
        oConsole.fPrint("  Statistics:       ", INFO, sStatisticsFilePath, NORMAL, " (%d bytes)" % len(sStatistics));
      oConsole.fPrint(); # and loop
    finally:
      oConsole.fUnlock();