Index: clang/cmake/caches/Fuchsia-stage2.cmake =================================================================== --- clang/cmake/caches/Fuchsia-stage2.cmake +++ clang/cmake/caches/Fuchsia-stage2.cmake @@ -293,6 +293,7 @@ llvm-symbolizer llvm-xray sancov + scan-build-py CACHE STRING "") set(LLVM_DISTRIBUTION_COMPONENTS Index: clang/tools/CMakeLists.txt =================================================================== --- clang/tools/CMakeLists.txt +++ clang/tools/CMakeLists.txt @@ -32,6 +32,7 @@ add_clang_subdirectory(clang-check) add_clang_subdirectory(clang-extdef-mapping) add_clang_subdirectory(scan-build) + add_clang_subdirectory(scan-build-py) add_clang_subdirectory(scan-view) endif() Index: clang/tools/scan-build-py/CMakeLists.txt =================================================================== --- /dev/null +++ clang/tools/scan-build-py/CMakeLists.txt @@ -0,0 +1,132 @@ +set (BinFiles + "analyze-build" + "intercept-build" + "scan-build") + +set (LibExecs + "analyze-c++" + "analyze-cc" + "intercept-c++" + "intercept-cc") + +set (LibScanbuild + "__init__.py" + "analyze.py" + "arguments.py" + "clang.py" + "compilation.py" + "intercept.py" + "report.py" + "shell.py") + +set (LibScanbuildResources + "scanview.css" + "selectable.js" + "sorttable.js") + +# libear is compiled dynamically in build_libear using the specified cc +# compiler. +set (LibEar + "__init__.py" + "config.h.in" + "ear.c") + +foreach(BinFile ${BinFiles}) + if ("${BinFile}" STREQUAL "scan-build") + # Need to rename scan-build to scan-build-py to prevent overwriting + # scan-build Perl implementation. + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/scan-build-py + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/bin/scan-build + ${CMAKE_BINARY_DIR}/bin/scan-build-py + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/scan-build) + install (PROGRAMS "bin/scan-build" + DESTINATION bin + RENAME scan-build-py + COMPONENT scan-build-py) + list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/scan-build-py) + else() + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/${BinFile} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile} + ${CMAKE_BINARY_DIR}/bin/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile}) + install(PROGRAMS bin/${BinFile} + DESTINATION bin + COMPONENT scan-build-py) + list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/${BinFile}) + endif() +endforeach() + +foreach(lib ${LibExecs}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/libexec/${lib} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/libexec + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/libexec/${lib} + ${CMAKE_BINARY_DIR}/libexec/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libexec/${lib}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/libexec/${lib}) + install(PROGRAMS libexec/${lib} + DESTINATION libexec + COMPONENT scan-build-py) +endforeach() + +foreach(lib ${LibScanbuild}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/lib/libscanbuild/${lib} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/lib + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/lib/libscanbuild + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/lib/libscanbuild/${lib} + ${CMAKE_BINARY_DIR}/lib/libscanbuild/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lib/libscanbuild/${lib}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/lib/libscanbuild/${lib}) + install(PROGRAMS lib/libscanbuild/${lib} + DESTINATION lib/libscanbuild + COMPONENT scan-build-py) +endforeach() + +foreach(resource ${LibScanbuildResources}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/lib/libscanbuild/resources/${resource} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/lib + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/lib/libscanbuild + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/lib/libscanbuild/resources + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/lib/libscanbuild/resources/${resource} + ${CMAKE_BINARY_DIR}/lib/libscanbuild/resources + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lib/libscanbuild/resources/${resource}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/lib/libscanbuild/resources/${resource}) + install(PROGRAMS lib/libscanbuild/resources/${resource} + DESTINATION lib/libscanbuild/resources + COMPONENT scan-build-py) +endforeach() + +foreach(lib ${LibEar}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/lib/libear/${lib} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/lib + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/lib/libear + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/lib/libear/${lib} + ${CMAKE_BINARY_DIR}/lib/libear/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lib/libear/${lib}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/lib/libear/${lib}) + install(PROGRAMS lib/libear/${lib} + DESTINATION lib/libear + COMPONENT scan-build-py) +endforeach() + +add_custom_target(scan-build-py ALL DEPENDS ${Depends}) +add_llvm_install_targets("install-scan-build-py" + DEPENDS scan-build-py + COMPONENT scan-build-py) Index: clang/tools/scan-build-py/bin/analyze-build =================================================================== --- clang/tools/scan-build-py/bin/analyze-build +++ clang/tools/scan-build-py/bin/analyze-build @@ -8,7 +8,7 @@ import sys import os.path this_dir = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.dirname(this_dir)) +sys.path.append(os.path.join(os.path.dirname(this_dir), 'lib')) from libscanbuild.analyze import analyze_build Index: clang/tools/scan-build-py/bin/intercept-build =================================================================== --- clang/tools/scan-build-py/bin/intercept-build +++ clang/tools/scan-build-py/bin/intercept-build @@ -8,7 +8,7 @@ import sys import os.path this_dir = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.dirname(this_dir)) +sys.path.append(os.path.join(os.path.dirname(this_dir), 'lib')) from libscanbuild.intercept import intercept_build Index: clang/tools/scan-build-py/bin/scan-build =================================================================== --- clang/tools/scan-build-py/bin/scan-build +++ clang/tools/scan-build-py/bin/scan-build @@ -8,7 +8,7 @@ import sys import os.path this_dir = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.dirname(this_dir)) +sys.path.append(os.path.join(os.path.dirname(this_dir), 'lib')) from libscanbuild.analyze import scan_build Index: clang/tools/scan-build-py/lib/libear/ear.c =================================================================== --- /dev/null +++ clang/tools/scan-build-py/lib/libear/ear.c @@ -0,0 +1,600 @@ +/* -*- coding: utf-8 -*- +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +/** + * This file implements a shared library. This library can be pre-loaded by + * the dynamic linker of the Operating System (OS). It implements a few function + * related to process creation. By pre-load this library the executed process + * uses these functions instead of those from the standard library. + * + * The idea here is to inject a logic before call the real methods. The logic is + * to dump the call into a file. To call the real method this library is doing + * the job of the dynamic linker. + * + * The only input for the log writing is about the destination directory. + * This is passed as environment variable. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP +#include +#endif + +#if defined HAVE_NSGETENVIRON +#include +#else +extern char **environ; +#endif + +#define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR" +#ifdef APPLE +#define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE" +#define ENV_PRELOAD "DYLD_INSERT_LIBRARIES" +#define ENV_SIZE 3 +#else +#define ENV_PRELOAD "LD_PRELOAD" +#define ENV_SIZE 2 +#endif + +#define DLSYM(TYPE_, VAR_, SYMBOL_) \ + union { \ + void *from; \ + TYPE_ to; \ + } cast; \ + if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \ + perror("bear: dlsym"); \ + exit(EXIT_FAILURE); \ + } \ + TYPE_ const VAR_ = cast.to; + +typedef char const *bear_env_t[ENV_SIZE]; + +static int bear_capture_env_t(bear_env_t *env); +static int bear_reset_env_t(bear_env_t *env); +static void bear_release_env_t(bear_env_t *env); +static char const **bear_update_environment(char *const envp[], + bear_env_t *env); +static char const **bear_update_environ(char const **in, char const *key, + char const *value); +static char **bear_get_environment(); +static void bear_report_call(char const *fun, char const *const argv[]); +static char const **bear_strings_build(char const *arg, va_list *ap); +static char const **bear_strings_copy(char const **const in); +static char const **bear_strings_append(char const **in, char const *e); +static size_t bear_strings_length(char const *const *in); +static void bear_strings_release(char const **); + +static bear_env_t env_names = {ENV_OUTPUT, ENV_PRELOAD +#ifdef ENV_FLAT + , + ENV_FLAT +#endif +}; + +static bear_env_t initial_env = {0, 0 +#ifdef ENV_FLAT + , + 0 +#endif +}; + +static int initialized = 0; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static void on_load(void) __attribute__((constructor)); +static void on_unload(void) __attribute__((destructor)); + +#ifdef HAVE_EXECVE +static int call_execve(const char *path, char *const argv[], + char *const envp[]); +#endif +#ifdef HAVE_EXECVP +static int call_execvp(const char *file, char *const argv[]); +#endif +#ifdef HAVE_EXECVPE +static int call_execvpe(const char *file, char *const argv[], + char *const envp[]); +#endif +#ifdef HAVE_EXECVP2 +static int call_execvP(const char *file, const char *search_path, + char *const argv[]); +#endif +#ifdef HAVE_EXECT +static int call_exect(const char *path, char *const argv[], char *const envp[]); +#endif +#ifdef HAVE_POSIX_SPAWN +static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]); +#endif +#ifdef HAVE_POSIX_SPAWNP +static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]); +#endif + +/* Initialization method to Captures the relevant environment variables. + */ + +static void on_load(void) { + pthread_mutex_lock(&mutex); + if (!initialized) + initialized = bear_capture_env_t(&initial_env); + pthread_mutex_unlock(&mutex); +} + +static void on_unload(void) { + pthread_mutex_lock(&mutex); + bear_release_env_t(&initial_env); + initialized = 0; + pthread_mutex_unlock(&mutex); +} + +/* These are the methods we are try to hijack. + */ + +#ifdef HAVE_EXECVE +int execve(const char *path, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execve(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECV +#ifndef HAVE_EXECVE +#error can not implement execv without execve +#endif +int execv(const char *path, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + char *const *envp = bear_get_environment(); + return call_execve(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECVPE +int execvpe(const char *file, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvpe(file, argv, envp); +} +#endif + +#ifdef HAVE_EXECVP +int execvp(const char *file, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvp(file, argv); +} +#endif + +#ifdef HAVE_EXECVP2 +int execvP(const char *file, const char *search_path, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvP(file, search_path, argv); +} +#endif + +#ifdef HAVE_EXECT +int exect(const char *path, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_exect(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECL +#ifndef HAVE_EXECVE +#error can not implement execl without execve +#endif +int execl(const char *path, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + char *const *envp = bear_get_environment(); + int const result = call_execve(path, (char *const *)argv, envp); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_EXECLP +#ifndef HAVE_EXECVP +#error can not implement execlp without execvp +#endif +int execlp(const char *file, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + int const result = call_execvp(file, (char *const *)argv); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_EXECLE +#ifndef HAVE_EXECVE +#error can not implement execle without execve +#endif +// int execle(const char *path, const char *arg, ..., char * const envp[]); +int execle(const char *path, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + char const **envp = va_arg(args, char const **); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + int const result = + call_execve(path, (char *const *)argv, (char *const *)envp); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWN +int posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], char *const envp[restrict]) { + bear_report_call(__func__, (char const *const *)argv); + return call_posix_spawn(pid, path, file_actions, attrp, argv, envp); +} +#endif + +#ifdef HAVE_POSIX_SPAWNP +int posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], char *const envp[restrict]) { + bear_report_call(__func__, (char const *const *)argv); + return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp); +} +#endif + +/* These are the methods which forward the call to the standard implementation. + */ + +#ifdef HAVE_EXECVE +static int call_execve(const char *path, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "execve"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(path, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_EXECVPE +static int call_execvpe(const char *file, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "execvpe"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(file, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_EXECVP +static int call_execvp(const char *file, char *const argv[]) { + typedef int (*func)(const char *file, char *const argv[]); + + DLSYM(func, fp, "execvp"); + + bear_env_t current_env; + bear_capture_env_t(¤t_env); + bear_reset_env_t(&initial_env); + int const result = (*fp)(file, argv); + bear_reset_env_t(¤t_env); + bear_release_env_t(¤t_env); + + return result; +} +#endif + +#ifdef HAVE_EXECVP2 +static int call_execvP(const char *file, const char *search_path, + char *const argv[]) { + typedef int (*func)(const char *, const char *, char *const *); + + DLSYM(func, fp, "execvP"); + + bear_env_t current_env; + bear_capture_env_t(¤t_env); + bear_reset_env_t(&initial_env); + int const result = (*fp)(file, search_path, argv); + bear_reset_env_t(¤t_env); + bear_release_env_t(¤t_env); + + return result; +} +#endif + +#ifdef HAVE_EXECT +static int call_exect(const char *path, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "exect"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(path, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWN +static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]) { + typedef int (*func)(pid_t *restrict, const char *restrict, + const posix_spawn_file_actions_t *, + const posix_spawnattr_t *restrict, char *const *restrict, + char *const *restrict); + + DLSYM(func, fp, "posix_spawn"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = + (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWNP +static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]) { + typedef int (*func)(pid_t *restrict, const char *restrict, + const posix_spawn_file_actions_t *, + const posix_spawnattr_t *restrict, char *const *restrict, + char *const *restrict); + + DLSYM(func, fp, "posix_spawnp"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = + (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +/* this method is to write log about the process creation. */ + +static void bear_report_call(char const *fun, char const *const argv[]) { + static int const GS = 0x1d; + static int const RS = 0x1e; + static int const US = 0x1f; + + if (!initialized) + return; + + pthread_mutex_lock(&mutex); + const char *cwd = getcwd(NULL, 0); + if (0 == cwd) { + perror("bear: getcwd"); + exit(EXIT_FAILURE); + } + char const *const out_dir = initial_env[0]; + size_t const path_max_length = strlen(out_dir) + 32; + char filename[path_max_length]; + if (-1 == + snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) { + perror("bear: snprintf"); + exit(EXIT_FAILURE); + } + FILE *fd = fopen(filename, "a+"); + if (0 == fd) { + perror("bear: fopen"); + exit(EXIT_FAILURE); + } + fprintf(fd, "%d%c", getpid(), RS); + fprintf(fd, "%d%c", getppid(), RS); + fprintf(fd, "%s%c", fun, RS); + fprintf(fd, "%s%c", cwd, RS); + size_t const argc = bear_strings_length(argv); + for (size_t it = 0; it < argc; ++it) { + fprintf(fd, "%s%c", argv[it], US); + } + fprintf(fd, "%c", GS); + if (fclose(fd)) { + perror("bear: fclose"); + exit(EXIT_FAILURE); + } + free((void *)cwd); + pthread_mutex_unlock(&mutex); +} + +/* update environment assure that chilren processes will copy the desired + * behaviour */ + +static int bear_capture_env_t(bear_env_t *env) { + int status = 1; + for (size_t it = 0; it < ENV_SIZE; ++it) { + char const *const env_value = getenv(env_names[it]); + char const *const env_copy = (env_value) ? strdup(env_value) : env_value; + (*env)[it] = env_copy; + status &= (env_copy) ? 1 : 0; + } + return status; +} + +static int bear_reset_env_t(bear_env_t *env) { + int status = 1; + for (size_t it = 0; it < ENV_SIZE; ++it) { + if ((*env)[it]) { + setenv(env_names[it], (*env)[it], 1); + } else { + unsetenv(env_names[it]); + } + } + return status; +} + +static void bear_release_env_t(bear_env_t *env) { + for (size_t it = 0; it < ENV_SIZE; ++it) { + free((void *)(*env)[it]); + (*env)[it] = 0; + } +} + +static char const **bear_update_environment(char *const envp[], + bear_env_t *env) { + char const **result = bear_strings_copy((char const **)envp); + for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it) + result = bear_update_environ(result, env_names[it], (*env)[it]); + return result; +} + +static char const **bear_update_environ(char const *envs[], char const *key, + char const *const value) { + // find the key if it's there + size_t const key_length = strlen(key); + char const **it = envs; + for (; (it) && (*it); ++it) { + if ((0 == strncmp(*it, key, key_length)) && (strlen(*it) > key_length) && + ('=' == (*it)[key_length])) + break; + } + // allocate a environment entry + size_t const value_length = strlen(value); + size_t const env_length = key_length + value_length + 2; + char *env = malloc(env_length); + if (0 == env) { + perror("bear: malloc [in env_update]"); + exit(EXIT_FAILURE); + } + if (-1 == snprintf(env, env_length, "%s=%s", key, value)) { + perror("bear: snprintf"); + exit(EXIT_FAILURE); + } + // replace or append the environment entry + if (it && *it) { + free((void *)*it); + *it = env; + return envs; + } + return bear_strings_append(envs, env); +} + +static char **bear_get_environment() { +#if defined HAVE_NSGETENVIRON + return *_NSGetEnviron(); +#else + return environ; +#endif +} + +/* util methods to deal with string arrays. environment and process arguments + * are both represented as string arrays. */ + +static char const **bear_strings_build(char const *const arg, va_list *args) { + char const **result = 0; + size_t size = 0; + for (char const *it = arg; it; it = va_arg(*args, char const *)) { + result = realloc(result, (size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + char const *copy = strdup(it); + if (0 == copy) { + perror("bear: strdup"); + exit(EXIT_FAILURE); + } + result[size++] = copy; + } + result = realloc(result, (size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + result[size++] = 0; + + return result; +} + +static char const **bear_strings_copy(char const **const in) { + size_t const size = bear_strings_length(in); + + char const **const result = malloc((size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: malloc"); + exit(EXIT_FAILURE); + } + + char const **out_it = result; + for (char const *const *in_it = in; (in_it) && (*in_it); ++in_it, ++out_it) { + *out_it = strdup(*in_it); + if (0 == *out_it) { + perror("bear: strdup"); + exit(EXIT_FAILURE); + } + } + *out_it = 0; + return result; +} + +static char const **bear_strings_append(char const **const in, + char const *const e) { + size_t size = bear_strings_length(in); + char const **result = realloc(in, (size + 2) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + result[size++] = e; + result[size++] = 0; + return result; +} + +static size_t bear_strings_length(char const *const *const in) { + size_t result = 0; + for (char const *const *it = in; (it) && (*it); ++it) + ++result; + return result; +} + +static void bear_strings_release(char const **in) { + for (char const *const *it = in; (it) && (*it); ++it) { + free((void *)*it); + } + free((void *)in); +} \ No newline at end of file Index: clang/tools/scan-build-py/lib/libscanbuild/arguments.py =================================================================== --- clang/tools/scan-build-py/lib/libscanbuild/arguments.py +++ clang/tools/scan-build-py/lib/libscanbuild/arguments.py @@ -62,6 +62,7 @@ """ Parse and validate command-line arguments for scan-build. """ from_build_command = True + logging.warning("hello") parser = create_analyze_parser(from_build_command) args = parser.parse_args() Index: clang/tools/scan-build-py/lib/libscanbuild/report.py =================================================================== --- clang/tools/scan-build-py/lib/libscanbuild/report.py +++ clang/tools/scan-build-py/lib/libscanbuild/report.py @@ -379,21 +379,22 @@ def parse_bug_plist(filename): """ Returns the generator of bugs from a single .plist file. """ - content = plistlib.readPlist(filename) - files = content.get('files') - for bug in content.get('diagnostics', []): - if len(files) <= int(bug['location']['file']): - logging.warning('Parsing bug from "%s" failed', filename) - continue - - yield { - 'result': filename, - 'bug_type': bug['type'], - 'bug_category': bug['category'], - 'bug_line': int(bug['location']['line']), - 'bug_path_length': int(bug['location']['col']), - 'bug_file': files[int(bug['location']['file'])] - } + with open(filename, 'rb') as fp: + content = plistlib.load(fp) + files = content.get('files') + for bug in content.get('diagnostics', []): + if len(files) <= int(bug['location']['file']): + logging.warning('Parsing bug from "%s" failed', filename) + continue + + yield { + 'result': filename, + 'bug_type': bug['type'], + 'bug_category': bug['category'], + 'bug_line': int(bug['location']['line']), + 'bug_path_length': int(bug['location']['col']), + 'bug_file': files[int(bug['location']['file'])] + } def parse_bug_html(filename): Index: clang/tools/scan-build-py/lib/libscanbuild/resources/selectable.js =================================================================== --- clang/tools/scan-build-py/lib/libscanbuild/resources/selectable.js +++ clang/tools/scan-build-py/lib/libscanbuild/resources/selectable.js @@ -1,7 +1,6 @@ -function SetDisplay(RowClass, DisplayVal) -{ +function SetDisplay(RowClass, DisplayVal) { var Rows = document.getElementsByTagName("tr"); - for ( var i = 0 ; i < Rows.length; ++i ) { + for (var i = 0; i < Rows.length; ++i) { if (Rows[i].className == RowClass) { Rows[i].style.display = DisplayVal; } @@ -10,24 +9,24 @@ function CopyCheckedStateToCheckButtons(SummaryCheckButton) { var Inputs = document.getElementsByTagName("input"); - for ( var i = 0 ; i < Inputs.length; ++i ) { + for (var i = 0; i < Inputs.length; ++i) { if (Inputs[i].type == "checkbox") { - if(Inputs[i] != SummaryCheckButton) { + if (Inputs[i] != SummaryCheckButton) { Inputs[i].checked = SummaryCheckButton.checked; Inputs[i].onclick(); - } + } } } } -function returnObjById( id ) { - if (document.getElementById) - var returnVar = document.getElementById(id); - else if (document.all) - var returnVar = document.all[id]; - else if (document.layers) - var returnVar = document.layers[id]; - return returnVar; +function returnObjById(id) { + if (document.getElementById) + var returnVar = document.getElementById(id); + else if (document.all) + var returnVar = document.all[id]; + else if (document.layers) + var returnVar = document.layers[id]; + return returnVar; } var NumUnchecked = 0; @@ -38,8 +37,7 @@ if (--NumUnchecked == 0) { returnObjById("AllBugsCheck").checked = true; } - } - else { + } else { SetDisplay(ClassName, "none"); NumUnchecked++; returnObjById("AllBugsCheck").checked = false; Index: clang/tools/scan-build-py/lib/libscanbuild/resources/sorttable.js =================================================================== --- /dev/null +++ clang/tools/scan-build-py/lib/libscanbuild/resources/sorttable.js @@ -0,0 +1,535 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + +var stIsIE = /*@cc_on!@*/ false; + +sorttable = { + init : function() { + // quit if this function has already been called + if (arguments.callee.done) + return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) + clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) + return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + }, + + makeSortable : function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the, table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) + table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) + return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backward compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i = 0; i < table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i = 0; i < sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i = 0; i < headrow.length; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match( + /\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { + override = mtch[1]; + } + if (mtch && typeof sorttable["sort_" + override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_" + override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table, i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i], "click", function(e) { + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE + ? ' 5' + : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE + ? ' 6' + : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = + cell.className.replace('sorttable_sorted_reverse', ''); + cell.className = cell.className.replace('sorttable_sorted', ''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { + sortfwdind.parentNode.removeChild(sortfwdind); + } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { + sortrevind.parentNode.removeChild(sortrevind); + } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = + stIsIE ? ' 6' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j = 0; j < rows.length; j++) { + row_array[row_array.length] = + [ sorttable.getInnerText(rows[j].cells[col]), rows[j] ]; + } + /* If you want a stable sort, uncomment the following line */ + sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + // row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + for (var j = 0; j < row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + + delete row_array; + }); + } + } + }, + + guessType : function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_alpha; + for (var i = 0; i < table.tBodies[0].rows.length; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[」$、]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText : function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse : function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i = 0; i < tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + for (var i = newrows.length - 1; i >= 0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric : function(a, b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g, '')); + if (isNaN(aa)) + aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g, '')); + if (isNaN(bb)) + bb = 0; + return aa - bb; + }, + sort_alpha : function(a, b) { + if (a[0] == b[0]) + return 0; + if (a[0] < b[0]) + return -1; + return 1; + }, + sort_ddmm : function(a, b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; + m = mtch[2]; + d = mtch[1]; + if (m.length == 1) + m = '0' + m; + if (d.length == 1) + d = '0' + d; + dt1 = y + m + d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; + m = mtch[2]; + d = mtch[1]; + if (m.length == 1) + m = '0' + m; + if (d.length == 1) + d = '0' + d; + dt2 = y + m + d; + if (dt1 == dt2) + return 0; + if (dt1 < dt2) + return -1; + return 1; + }, + sort_mmdd : function(a, b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; + d = mtch[2]; + m = mtch[1]; + if (m.length == 1) + m = '0' + m; + if (d.length == 1) + d = '0' + d; + dt1 = y + m + d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; + d = mtch[2]; + m = mtch[1]; + if (m.length == 1) + m = '0' + m; + if (d.length == 1) + d = '0' + d; + dt2 = y + m + d; + if (dt1 == dt2) + return 0; + if (dt1 < dt2) + return -1; + return 1; + }, + + shaker_sort : function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while (swap) { + swap = false; + for (var i = b; i < t; ++i) { + if (comp_func(list[i], list[i + 1]) > 0) { + var q = list[i]; + list[i] = list[i + 1]; + list[i + 1] = q; + swap = true; + } + } // for + t--; + + if (!swap) + break; + + for (var i = t; i > b; --i) { + if (comp_func(list[i], list[i - 1]) < 0) { + var q = list[i]; + list[i] = list[i - 1]; + list[i - 1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write(" to your HTML - Add class="sortable" to any table you'd like to make sortable - Click on the headers to sort - - Thanks to many, many people for contributions and suggestions. - Licenced as X11: http://www.kryogenix.org/code/browser/licence.html - This basically means: do what you want with it. -*/ - - -var stIsIE = /*@cc_on!@*/false; - -sorttable = { - init: function() { - // quit if this function has already been called - if (arguments.callee.done) return; - // flag this function so we don't do the same thing twice - arguments.callee.done = true; - // kill the timer - if (_timer) clearInterval(_timer); - - if (!document.createElement || !document.getElementsByTagName) return; - - sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; - - forEach(document.getElementsByTagName('table'), function(table) { - if (table.className.search(/\bsortable\b/) != -1) { - sorttable.makeSortable(table); - } - }); - - }, - - makeSortable: function(table) { - if (table.getElementsByTagName('thead').length == 0) { - // table doesn't have a tHead. Since it should have, create one and - // put the first table row in it. - the = document.createElement('thead'); - the.appendChild(table.rows[0]); - table.insertBefore(the,table.firstChild); - } - // Safari doesn't support table.tHead, sigh - if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; - - if (table.tHead.rows.length != 1) return; // can't cope with two header rows - - // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as - // "total" rows, for example). This is B&R, since what you're supposed - // to do is put them in a tfoot. So, if there are sortbottom rows, - // for backward compatibility, move them to tfoot (creating it if needed). - sortbottomrows = []; - for (var i=0; i5' : ' ▴'; - this.appendChild(sortrevind); - return; - } - if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { - // if we're already sorted by this column in reverse, just - // re-reverse the table, which is quicker - sorttable.reverse(this.sorttable_tbody); - this.className = this.className.replace('sorttable_sorted_reverse', - 'sorttable_sorted'); - this.removeChild(document.getElementById('sorttable_sortrevind')); - sortfwdind = document.createElement('span'); - sortfwdind.id = "sorttable_sortfwdind"; - sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; - this.appendChild(sortfwdind); - return; - } - - // remove sorttable_sorted classes - theadrow = this.parentNode; - forEach(theadrow.childNodes, function(cell) { - if (cell.nodeType == 1) { // an element - cell.className = cell.className.replace('sorttable_sorted_reverse',''); - cell.className = cell.className.replace('sorttable_sorted',''); - } - }); - sortfwdind = document.getElementById('sorttable_sortfwdind'); - if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } - sortrevind = document.getElementById('sorttable_sortrevind'); - if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } - - this.className += ' sorttable_sorted'; - sortfwdind = document.createElement('span'); - sortfwdind.id = "sorttable_sortfwdind"; - sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; - this.appendChild(sortfwdind); - - // build an array to sort. This is a Schwartzian transform thing, - // i.e., we "decorate" each row with the actual sort key, - // sort based on the sort keys, and then put the rows back in order - // which is a lot faster because you only do getInnerText once per row - row_array = []; - col = this.sorttable_columnindex; - rows = this.sorttable_tbody.rows; - for (var j=0; j 12) { - // definitely dd/mm - return sorttable.sort_ddmm; - } else if (second > 12) { - return sorttable.sort_mmdd; - } else { - // looks like a date, but we can't tell which, so assume - // that it's dd/mm (English imperialism!) and keep looking - sortfn = sorttable.sort_ddmm; - } - } - } - } - return sortfn; - }, - - getInnerText: function(node) { - // gets the text we want to use for sorting for a cell. - // strips leading and trailing whitespace. - // this is *not* a generic getInnerText function; it's special to sorttable. - // for example, you can override the cell text with a customkey attribute. - // it also gets .value for fields. - - hasInputs = (typeof node.getElementsByTagName == 'function') && - node.getElementsByTagName('input').length; - - if (node.getAttribute("sorttable_customkey") != null) { - return node.getAttribute("sorttable_customkey"); - } - else if (typeof node.textContent != 'undefined' && !hasInputs) { - return node.textContent.replace(/^\s+|\s+$/g, ''); - } - else if (typeof node.innerText != 'undefined' && !hasInputs) { - return node.innerText.replace(/^\s+|\s+$/g, ''); - } - else if (typeof node.text != 'undefined' && !hasInputs) { - return node.text.replace(/^\s+|\s+$/g, ''); - } - else { - switch (node.nodeType) { - case 3: - if (node.nodeName.toLowerCase() == 'input') { - return node.value.replace(/^\s+|\s+$/g, ''); - } - case 4: - return node.nodeValue.replace(/^\s+|\s+$/g, ''); - break; - case 1: - case 11: - var innerText = ''; - for (var i = 0; i < node.childNodes.length; i++) { - innerText += sorttable.getInnerText(node.childNodes[i]); - } - return innerText.replace(/^\s+|\s+$/g, ''); - break; - default: - return ''; - } - } - }, - - reverse: function(tbody) { - // reverse the rows in a tbody - newrows = []; - for (var i=0; i=0; i--) { - tbody.appendChild(newrows[i]); - } - delete newrows; - }, - - /* sort functions - each sort function takes two parameters, a and b - you are comparing a[0] and b[0] */ - sort_numeric: function(a,b) { - aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); - if (isNaN(aa)) aa = 0; - bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); - if (isNaN(bb)) bb = 0; - return aa-bb; - }, - sort_alpha: function(a,b) { - if (a[0]==b[0]) return 0; - if (a[0] 0 ) { - var q = list[i]; list[i] = list[i+1]; list[i+1] = q; - swap = true; - } - } // for - t--; - - if (!swap) break; - - for(var i = t; i > b; --i) { - if ( comp_func(list[i], list[i-1]) < 0 ) { - var q = list[i]; list[i] = list[i-1]; list[i-1] = q; - swap = true; - } - } // for - b++; - - } // while(swap) - } -} - -/* ****************************************************************** - Supporting functions: bundled here to avoid depending on a library - ****************************************************************** */ - -// Dean Edwards/Matthias Miller/John Resig - -/* for Mozilla/Opera9 */ -if (document.addEventListener) { - document.addEventListener("DOMContentLoaded", sorttable.init, false); -} - -/* for Internet Explorer */ -/*@cc_on @*/ -/*@if (@_win32) - document.write("