Beispiel #1
0
def run_analyzer(opts, continuation=report_failure):
    # type: (...) -> Dict[str, Any]
    """ It assembles the analysis command line and executes it. Capture the
    output of the analysis and returns with it. If failure reports are
    requested, it calls the continuation to generate it. """

    def target():
        # type: () -> str
        """ Creates output file name for reports. """
        if opts['output_format'].startswith('plist'):
            (handle, name) = tempfile.mkstemp(prefix='report-',
                                              suffix='.plist',
                                              dir=opts['output_dir'])
            os.close(handle)
            return name
        return opts['output_dir']

    try:
        cwd = opts['directory']
        cmd = get_arguments([opts['clang'], '--analyze'] +
                            opts['direct_args'] + opts['flags'] +
                            [opts['source'], '-o', target()],
                            cwd)
        output = run_command(cmd, cwd=cwd)
        return {'error_output': output, 'exit_code': 0}
    except OSError:
        message = 'failed to execute "{0}"'.format(opts['clang'])
        return {'error_output': message, 'exit_code': 127}
    except subprocess.CalledProcessError as ex:
        logging.warning('analysis failed: %s', exc_info=True)
        result = {'error_output': ex.output, 'exit_code': ex.returncode}
        if opts.get('output_failures', False):
            opts.update(result)
            continuation(opts)
        return result
Beispiel #2
0
def run_analyzer(opts, continuation=report_failure):
    """ It assembles the analysis command line and executes it. Capture the
    output of the analysis and returns with it. If failure reports are
    requested, it calls the continuation to generate it. """

    def target():
        """ Creates output file name for reports. """
        if opts['output_format'] in {
                'plist',
                'plist-html',
                'plist-multi-file'}:
            (handle, name) = tempfile.mkstemp(prefix='report-',
                                              suffix='.plist',
                                              dir=opts['output_dir'])
            os.close(handle)
            return name
        return opts['output_dir']

    try:
        cwd = opts['directory']
        cmd = get_arguments([opts['clang'], '--analyze'] +
                            opts['direct_args'] + opts['flags'] +
                            [opts['file'], '-o', target()],
                            cwd)
        output = run_command(cmd, cwd=cwd)
        return {'error_output': output, 'exit_code': 0}
    except subprocess.CalledProcessError as ex:
        result = {'error_output': ex.output, 'exit_code': ex.returncode}
        if opts.get('output_failures', False):
            opts.update(result)
            continuation(opts)
        return result
Beispiel #3
0
def run_analyzer(opts, continuation=report_failure):
    # type: (...) -> Dict[str, Any]
    """ It assembles the analysis command line and executes it. Capture the
    output of the analysis and returns with it. If failure reports are
    requested, it calls the continuation to generate it. """

    def target():
        # type: () -> str
        """ Creates output file name for reports. """
        if opts['output_format'].startswith('plist'):
            (handle, name) = tempfile.mkstemp(prefix='report-',
                                              suffix='.plist',
                                              dir=opts['output_dir'])
            os.close(handle)
            return name
        return opts['output_dir']

    try:
        cwd = opts['directory']
        cmd = get_arguments([opts['clang'], '--analyze'] +
                            opts['direct_args'] + opts['flags'] +
                            [opts['source'], '-o', target()],
                            cwd)
        output = run_command(cmd, cwd=cwd)
        return {'error_output': output, 'exit_code': 0}
    except OSError:
        message = 'failed to execute "{0}"'.format(opts['clang'])
        return {'error_output': [message], 'exit_code': 127}
    except subprocess.CalledProcessError as ex:
        logging.warning('analysis failed', exc_info=True)
        result = {'error_output': ex.output, 'exit_code': ex.returncode}
        if opts.get('output_failures', False):
            opts.update(result)
            continuation(opts)
        return result
Beispiel #4
0
def run_analyzer(opts, continuation=report_failure):
    """ It assembles the analysis command line and executes it. Capture the
    output of the analysis and returns with it. If failure reports are
    requested, it calls the continuation to generate it. """

    def target():
        """ Creates output file name for reports. """
        if opts['output_format'] in {'plist', 'plist-html'}:
            (handle, name) = tempfile.mkstemp(prefix='report-',
                                              suffix='.plist',
                                              dir=opts['output_dir'])
            os.close(handle)
            return name
        return opts['output_dir']

    try:
        cwd = opts['directory']
        cmd = get_arguments([opts['clang'], '--analyze'] +
                            opts['direct_args'] + opts['flags'] +
                            [opts['file'], '-o', target()],
                            cwd)
        output = run_command(cmd, cwd=cwd)
        return {'error_output': output, 'exit_code': 0}
    except subprocess.CalledProcessError as ex:
        result = {'error_output': ex.output, 'exit_code': ex.returncode}
        if opts.get('output_failures', False):
            opts.update(result)
            continuation(opts)
        return result
Beispiel #5
0
def run_analyzer(opts, continuation=report_failure):
    """ It assembles the analysis command line and executes it. Capture the
    output of the analysis and returns with it. If failure reports are
    requested, it calls the continuation to generate it. """

    cwd = opts['directory']
    cmd = get_arguments([opts['clang']] + opts['analyze'] + opts['output'],
                        cwd)
    logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
    child = subprocess.Popen(cmd,
                             cwd=cwd,
                             universal_newlines=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
    output = child.stdout.readlines()
    child.stdout.close()
    # do report details if it were asked
    child.wait()
    if opts.get('output_failures', False) and child.returncode:
        error_type = 'crash' if child.returncode & 127 else 'other_error'
        opts.update({
            'error_type': error_type,
            'error_output': output,
            'exit_code': child.returncode
        })
        return continuation(opts)
    return {'error_output': output, 'exit_code': child.returncode}
Beispiel #6
0
def report_failure(opts):
    # type: (Dict[str, Any]) -> None
    """ Create report when analyzer failed.

    The major report is the preprocessor output. The output filename generated
    randomly. The compiler output also captured into '.stderr.txt' file.
    And some more execution context also saved into '.info.txt' file. """
    def extension():
        # type: () -> str
        """ Generate preprocessor file extension. """

        mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'}
        return mapping.get(opts['language'], '.i')

    def destination():
        # type: () -> str
        """ Creates failures directory if not exits yet. """

        failures_dir = os.path.join(opts['output_dir'], 'failures')
        if not os.path.isdir(failures_dir):
            os.makedirs(failures_dir)
        return failures_dir

    # Classify error type: when Clang terminated by a signal it's a 'Crash'.
    # (python subprocess Popen.returncode is negative when child terminated
    # by signal.) Everything else is 'Other Error'.
    error = 'crash' if opts['exit_code'] < 0 else 'other_error'
    # Create preprocessor output file name. (This is blindly following the
    # Perl implementation.)
    (fd, name) = tempfile.mkstemp(suffix=extension(),
                                  prefix='clang_' + error + '_',
                                  dir=destination())
    os.close(fd)
    # Execute Clang again, but run the syntax check only.
    try:
        cwd = opts['directory']
        cmd = get_arguments([opts['clang'], '-fsyntax-only', '-E'] +
                            opts['prepend-flags'] + opts['flags'] +
                            opts['append-flags'] +
                            [opts['source'], '-o', name], cwd)
        run_command(cmd, cwd=cwd)
        # write general information about the crash
        with open(name + '.info.txt', 'w') as handle:
            handle.write(opts['source'] + os.linesep)
            handle.write(error.title().replace('_', ' ') + os.linesep)
            handle.write(' '.join(cmd) + os.linesep)
            handle.write(' '.join(platform.uname()) + os.linesep)
            handle.write(get_version(opts['clang']))
            handle.close()
        # write the captured output too
        with open(name + '.stderr.txt', 'w') as handle:
            for line in opts['error_output']:
                handle.write(line)
            handle.close()
    except (OSError, subprocess.CalledProcessError):
        logging.warning('failed to report failure', exc_info=True)
Beispiel #7
0
def report_failure(opts):
    # type: (Dict[str, Any]) -> None
    """ Create report when analyzer failed.

    The major report is the preprocessor output. The output filename generated
    randomly. The compiler output also captured into '.stderr.txt' file.
    And some more execution context also saved into '.info.txt' file. """

    def extension():
        # type: () -> str
        """ Generate preprocessor file extension. """

        mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'}
        return mapping.get(opts['language'], '.i')

    def destination():
        # type: () -> str
        """ Creates failures directory if not exits yet. """

        failures_dir = os.path.join(opts['output_dir'], 'failures')
        if not os.path.isdir(failures_dir):
            os.makedirs(failures_dir)
        return failures_dir

    # Classify error type: when Clang terminated by a signal it's a 'Crash'.
    # (python subprocess Popen.returncode is negative when child terminated
    # by signal.) Everything else is 'Other Error'.
    error = 'crash' if opts['exit_code'] < 0 else 'other_error'
    # Create preprocessor output file name. (This is blindly following the
    # Perl implementation.)
    (fd, name) = tempfile.mkstemp(suffix=extension(),
                                  prefix='clang_' + error + '_',
                                  dir=destination())
    os.close(fd)
    # Execute Clang again, but run the syntax check only.
    try:
        cwd = opts['directory']
        cmd = get_arguments([opts['clang'], '-fsyntax-only', '-E'] +
                            opts['flags'] + [opts['source'], '-o', name], cwd)
        run_command(cmd, cwd=cwd)
        # write general information about the crash
        with open(name + '.info.txt', 'w') as handle:
            handle.write(opts['source'] + os.linesep)
            handle.write(error.title().replace('_', ' ') + os.linesep)
            handle.write(' '.join(cmd) + os.linesep)
            handle.write(' '.join(platform.uname()) + os.linesep)
            handle.write(get_version(opts['clang']))
            handle.close()
        # write the captured output too
        with open(name + '.stderr.txt', 'w') as handle:
            for line in opts['error_output']:
                handle.write(line)
            handle.close()
    except (OSError, subprocess.CalledProcessError):
        logging.warning('failed to report failure', exc_info=True)
Beispiel #8
0
    def test_get_clang_arguments(self):
        with fixtures.TempDir() as tmpdir:
            filename = os.path.join(tmpdir, 'test.c')
            with open(filename, 'w') as handle:
                handle.write('')

            result = sut.get_arguments(
                ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'],
                tmpdir)

            self.assertIn('NDEBUG', result)
            self.assertIn('var="this is it"', result)
Beispiel #9
0
    def test_get_clang_arguments(self):
        with libear.TemporaryDirectory() as tmpdir:
            filename = os.path.join(tmpdir, 'test.c')
            with open(filename, 'w') as handle:
                handle.write('')

            result = sut.get_arguments(
                ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'],
                tmpdir)

            self.assertTrue('NDEBUG' in result)
            self.assertTrue('var="this is it"' in result)
Beispiel #10
0
    def test_get_clang_arguments(self):
        with libear.TemporaryDirectory() as tmpdir:
            filename = os.path.join(tmpdir, 'test.c')
            with open(filename, 'w') as handle:
                handle.write('')

            result = sut.get_arguments(
                ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'],
                tmpdir)

            self.assertTrue('NDEBUG' in result)
            self.assertTrue('var="this is it"' in result)
Beispiel #11
0
def report_failure(opts):
    """ Create report when analyzer failed.

    The major report is the preprocessor output. The output filename generated
    randomly. The compiler output also captured into '.stderr.txt' file.
    And some more execution context also saved into '.info.txt' file. """

    def extension(opts):
        """ Generate preprocessor file extension. """

        mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'}
        return mapping.get(opts['language'], '.i')

    def destination(opts):
        """ Creates failures directory if not exits yet. """

        name = os.path.join(opts['output_dir'], 'failures')
        if not os.path.isdir(name):
            os.makedirs(name)
        return name

    error = opts['error_type']
    (handle, name) = tempfile.mkstemp(suffix=extension(opts),
                                      prefix='clang_' + error + '_',
                                      dir=destination(opts))
    os.close(handle)
    cwd = opts['directory']
    cmd = get_arguments([opts['clang'], '-fsyntax-only', '-E'] +
                        opts['flags'] + [opts['file'], '-o', name], cwd)
    logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
    subprocess.call(cmd, cwd=cwd)
    # write general information about the crash
    with open(name + '.info.txt', 'w') as handle:
        handle.write(opts['file'] + os.linesep)
        handle.write(error.title().replace('_', ' ') + os.linesep)
        handle.write(' '.join(cmd) + os.linesep)
        handle.write(' '.join(os.uname()) + os.linesep)
        handle.write(get_version(opts['clang']))
        handle.close()
    # write the captured output too
    with open(name + '.stderr.txt', 'w') as handle:
        handle.writelines(opts['error_output'])
        handle.close()
    # return with the previous step exit code and output
    return {
        'error_output': opts['error_output'],
        'exit_code': opts['exit_code']
    }
Beispiel #12
0
def report_failure(opts):
    """ Create report when analyzer failed.

    The major report is the preprocessor output. The output filename generated
    randomly. The compiler output also captured into '.stderr.txt' file.
    And some more execution context also saved into '.info.txt' file. """

    def extension(opts):
        """ Generate preprocessor file extension. """

        mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'}
        return mapping.get(opts['language'], '.i')

    def destination(opts):
        """ Creates failures directory if not exits yet. """

        name = os.path.join(opts['output_dir'], 'failures')
        if not os.path.isdir(name):
            os.makedirs(name)
        return name

    error = opts['error_type']
    (handle, name) = tempfile.mkstemp(suffix=extension(opts),
                                      prefix='clang_' + error + '_',
                                      dir=destination(opts))
    os.close(handle)
    cwd = opts['directory']
    cmd = get_arguments([opts['clang']] + opts['report'] + ['-o', name], cwd)
    logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
    subprocess.call(cmd, cwd=cwd)

    with open(name + '.info.txt', 'w') as handle:
        handle.write(opts['file'] + os.linesep)
        handle.write(error.title().replace('_', ' ') + os.linesep)
        handle.write(' '.join(cmd) + os.linesep)
        handle.write(' '.join(os.uname()) + os.linesep)
        handle.write(get_version(cmd[0]))
        handle.close()

    with open(name + '.stderr.txt', 'w') as handle:
        handle.writelines(opts['error_output'])
        handle.close()

    return {
        'error_output': opts['error_output'],
        'exit_code': opts['exit_code']
    }
Beispiel #13
0
def run_analyzer(opts, continuation=report_failure):
    """ It assembles the analysis command line and executes it. Capture the
    output of the analysis and returns with it. If failure reports are
    requested, it calls the continuation to generate it. """

    cwd = opts["directory"]
    cmd = get_arguments([opts["clang"]] + opts["analyze"] + opts["output"], cwd)
    logging.debug("exec command in %s: %s", cwd, " ".join(cmd))
    child = subprocess.Popen(cmd, cwd=cwd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = child.stdout.readlines()
    child.stdout.close()
    # do report details if it were asked
    child.wait()
    if opts.get("output_failures", False) and child.returncode:
        error_type = "crash" if child.returncode & 127 else "other_error"
        opts.update({"error_type": error_type, "error_output": output, "exit_code": child.returncode})
        return continuation(opts)
    return {"error_output": output, "exit_code": child.returncode}
Beispiel #14
0
def report_failure(opts):
    """ Create report when analyzer failed.

    The major report is the preprocessor output. The output filename generated
    randomly. The compiler output also captured into '.stderr.txt' file.
    And some more execution context also saved into '.info.txt' file. """

    def extension(opts):
        """ Generate preprocessor file extension. """

        mapping = {"objective-c++": ".mii", "objective-c": ".mi", "c++": ".ii"}
        return mapping.get(opts["language"], ".i")

    def destination(opts):
        """ Creates failures directory if not exits yet. """

        name = os.path.join(opts["output_dir"], "failures")
        if not os.path.isdir(name):
            os.makedirs(name)
        return name

    error = opts["error_type"]
    (handle, name) = tempfile.mkstemp(suffix=extension(opts), prefix="clang_" + error + "_", dir=destination(opts))
    os.close(handle)
    cwd = opts["directory"]
    cmd = get_arguments([opts["clang"]] + opts["report"] + ["-o", name], cwd)
    logging.debug("exec command in %s: %s", cwd, " ".join(cmd))
    subprocess.call(cmd, cwd=cwd)

    with open(name + ".info.txt", "w") as handle:
        handle.write(opts["file"] + os.linesep)
        handle.write(error.title().replace("_", " ") + os.linesep)
        handle.write(" ".join(cmd) + os.linesep)
        handle.write(" ".join(os.uname()) + os.linesep)
        handle.write(get_version(cmd[0]))
        handle.close()

    with open(name + ".stderr.txt", "w") as handle:
        handle.writelines(opts["error_output"])
        handle.close()

    return {"error_output": opts["error_output"], "exit_code": opts["exit_code"]}
Beispiel #15
0
def run_analyzer(opts, continuation=report_failure):
    """ It assembles the analysis command line and executes it. Capture the
    output of the analysis and returns with it. If failure reports are
    requested, it calls the continuation to generate it. """

    def output():
        """ Creates output file name for reports. """
        if opts['output_format'] in {'plist', 'plist-html'}:
            (handle, name) = tempfile.mkstemp(prefix='report-',
                                              suffix='.plist',
                                              dir=opts['output_dir'])
            os.close(handle)
            return name
        return opts['output_dir']

    cwd = opts['directory']
    cmd = get_arguments([opts['clang'], '--analyze'] + opts['direct_args'] +
                        opts['flags'] + [opts['file'], '-o', output()],
                        cwd)
    logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
    child = subprocess.Popen(cmd,
                             cwd=cwd,
                             universal_newlines=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
    output = child.stdout.readlines()
    child.stdout.close()
    # do report details if it were asked
    child.wait()
    if opts.get('output_failures', False) and child.returncode:
        error_type = 'crash' if child.returncode & 127 else 'other_error'
        opts.update({
            'error_type': error_type,
            'error_output': output,
            'exit_code': child.returncode
        })
        return continuation(opts)
    # return the output for logging and exit code for testing
    return {'error_output': output, 'exit_code': child.returncode}
Beispiel #16
0
def run_analyzer(opts, continuation=report_failure):
    """ It assembles the analysis command line and executes it. Capture the
    output of the analysis and returns with it. If failure reports are
    requested, it calls the continuation to generate it. """
    def output():
        """ Creates output file name for reports. """
        if opts['output_format'] in {'plist', 'plist-html'}:
            (handle, name) = tempfile.mkstemp(prefix='report-',
                                              suffix='.plist',
                                              dir=opts['output_dir'])
            os.close(handle)
            return name
        return opts['output_dir']

    cwd = opts['directory']
    cmd = get_arguments([opts['clang'], '--analyze'] + opts['direct_args'] +
                        opts['flags'] +
                        [opts['file'], '-o', output()], cwd)
    logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
    child = subprocess.Popen(cmd,
                             cwd=cwd,
                             universal_newlines=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
    output = child.stdout.readlines()
    child.stdout.close()
    # do report details if it were asked
    child.wait()
    if opts.get('output_failures', False) and child.returncode:
        error_type = 'crash' if child.returncode & 127 else 'other_error'
        opts.update({
            'error_type': error_type,
            'error_output': output,
            'exit_code': child.returncode
        })
        return continuation(opts)
    # return the output for logging and exit code for testing
    return {'error_output': output, 'exit_code': child.returncode}
Beispiel #17
0
 def test_get_clang_arguments_fails(self):
     with self.assertRaises(Exception):
         sut.get_arguments(['clang', '-x', 'c', 'notexist.c'], '.')
Beispiel #18
0
 def test_get_clang_arguments_fails_badly(self):
     with self.assertRaises(OSError):
         sut.get_arguments(['notexist'], '.')
Beispiel #19
0
 def test_get_clang_arguments_fails(self):
     with self.assertRaises(Exception):
         sut.get_arguments(['clang', '-x', 'c', 'notexist.c'], '.')
Beispiel #20
0
 def test_get_clang_arguments_fails_badly(self):
     with self.assertRaises(OSError):
         sut.get_arguments(['notexist'], '.')