diff --git a/infer/bin/inferlib.py b/infer/bin/inferlib.py index cb3fd7e1a..d76246538 100644 --- a/infer/bin/inferlib.py +++ b/infer/bin/inferlib.py @@ -88,6 +88,8 @@ base_group.add_argument('-nf', '--no-filtering', action='store_true', help='''Also show the results from the experimental checks. Warning: some checks may contain many false alarms''') +base_group.add_argument('-l', '--llvm', action='store_true', + help='Analyze C or C++ file using LLVM translation') base_group.add_argument('--log_to_stderr', action='store_true', help='''When set, all logging will go to stderr instead diff --git a/infer/lib/capture/util.py b/infer/lib/capture/util.py index 3af9259b2..af11c5bb4 100644 --- a/infer/lib/capture/util.py +++ b/infer/lib/capture/util.py @@ -140,6 +140,8 @@ def get_clang_frontend_envvars(args): env_vars['FCP_DEBUG_MODE'] = '1' if args.no_failures_allowed: env_vars['FCP_REPORT_FRONTEND_FAILURE'] = '1' + if args.llvm: + env_vars['LLVM_MODE'] = '1' # export an env variable with all the arguments to pass to InferClang env_vars['FCP_INFER_FRONTEND_ARGS'] = ' '.join(frontend_args) diff --git a/infer/lib/clang/clang_general_wrapper b/infer/lib/clang/clang_general_wrapper index 4020b58ba..953a686c7 100755 --- a/infer/lib/clang/clang_general_wrapper +++ b/infer/lib/clang/clang_general_wrapper @@ -117,21 +117,29 @@ then else ADD_PLUGIN_FLAG="-add-plugin" fi - ATTACH_PLUGIN="1" + if [ -z "$LLVM_MODE" ]; then + ATTACH_PLUGIN="1" + fi IFS=$'\n' - EXTRA_ARGS=("-Xclang" "-load" - "-Xclang" "${PLUGIN_PATH}" - "-Xclang" "$ADD_PLUGIN_FLAG" - "-Xclang" "${PLUGIN_NAME}" - "-Xclang" "-plugin-arg-${PLUGIN_NAME}" - "-Xclang" "-" - "-Xclang" "-plugin-arg-${PLUGIN_NAME}" - "-Xclang" "PREPEND_CURRENT_DIR=1") + if [ -n "$ATTACH_PLUGIN" ]; then + EXTRA_ARGS=("-Xclang" "-load" + "-Xclang" "${PLUGIN_PATH}" + "-Xclang" "$ADD_PLUGIN_FLAG" + "-Xclang" "${PLUGIN_NAME}" + "-Xclang" "-plugin-arg-${PLUGIN_NAME}" + "-Xclang" "-" + "-Xclang" "-plugin-arg-${PLUGIN_NAME}" + "-Xclang" "PREPEND_CURRENT_DIR=1") + fi if [ -n "$SYNTAX_ONLY" ]; then EXTRA_ARGS+=("-fsyntax-only") fi unset IFS + if [ -n "$LLVM_MODE" ]; then + EXTRA_ARGS+=("-o" "-" "-g" "-S" "-emit-llvm") + fi + # using always the original clang command for several reasons: # - to avoid handling the presence/absence of -Xclang if the standard command is used # - to emit the same command that was captured by this wrapper @@ -140,7 +148,7 @@ then fi fi -if [ -n "$ATTACH_PLUGIN" ]; then +if [ -n "$ATTACH_PLUGIN" ] || [ -n "$LLVM_MODE" ]; then FOBJC_ARC_FLAG=$(has_flag "-fobjc-arc" "${INPUT_ARGUMENTS[@]}") LANGUAGE=$(get_option_argument "-x" "${INPUT_ARGUMENTS[@]}") @@ -148,37 +156,43 @@ if [ -n "$ATTACH_PLUGIN" ]; then if [ "$FOBJC_ARC_FLAG" == 0 ]; then INFER_FRONTEND_ARGS+=("-fobjc-arc"); fi [[ "$SOURCE_FILE" = /* ]] || { SOURCE_FILE="${CWD}/$SOURCE_FILE"; } - INFERCLANG_CMD=( - "${BIN_DIR}/InferClang" - "-c" "$SOURCE_FILE" - "-results_dir" "$RESULTS_DIR" - "${INFER_FRONTEND_ARGS[@]}") - - INFERCLANG_LOG_FILE="/dev/null" - - if [ -n "$DEBUG_MODE" ]; then - # Emit the clang command with the extra args piped to InferClang - echo "${CLANG_CMD[@]} " \ - "| tee ${OBJECT_FILENAME}.biniou " \ - "| ${INFERCLANG_CMD[@]}" \ - > "${OBJECT_FILENAME}${CMD_FILE_EXT}" - echo "bdump -x -d ${ETC_DIR}/clang_ast.dict -w '!!DUMMY!!' ${OBJECT_FILENAME}.biniou " \ - "> ${OBJECT_FILENAME}.bdump" \ - >> "${OBJECT_FILENAME}${CMD_FILE_EXT}" - # Emit the InferClang cmd used to run the frontend - INFERCLANG_LOG_FILE="${OBJECT_FILENAME}${INFERCLANG_LOG_FILE_EXT}" - echo "${INFERCLANG_CMD[@]}" > "$INFERCLANG_LOG_FILE" + + if [ -n "$LLVM_MODE" ]; then + INFER_FRONTEND_CMD=("${BIN_DIR}/InferLLVM") + INFER_FRONTEND_LOG_FILE="/dev/stdout" + else + INFER_FRONTEND_CMD=( + "${BIN_DIR}/InferClang" + "-c" "$SOURCE_FILE" + "-results_dir" "$RESULTS_DIR" + "${INFER_FRONTEND_ARGS[@]}") + + if [ -n "$DEBUG_MODE" ]; then + # Emit the clang command with the extra args piped to InferClang + echo "${CLANG_CMD[@]} " \ + "| tee ${OBJECT_FILENAME}.biniou " \ + "| ${INFER_FRONTEND_CMD[@]}" \ + > "${OBJECT_FILENAME}${CMD_FILE_EXT}" + echo "bdump -x -d ${ETC_DIR}/clang_ast.dict -w '!!DUMMY!!' ${OBJECT_FILENAME}.biniou " \ + "> ${OBJECT_FILENAME}.bdump" \ + >> "${OBJECT_FILENAME}${CMD_FILE_EXT}" + # Emit the InferClang cmd used to run the frontend + INFER_FRONTEND_LOG_FILE="${OBJECT_FILENAME}${INFERCLANG_LOG_FILE_EXT}" + echo "${INFER_FRONTEND_CMD[@]}" > "$INFER_FRONTEND_LOG_FILE" + else + INFER_FRONTEND_LOG_FILE="/dev/null" + fi fi - # run clang and pipe its output to InferClang, or flush it in case the latter crashes - "${CLANG_CMD[@]}" | ("${INFERCLANG_CMD[@]}" || { EC=$?; cat > /dev/null; exit $EC; }) >> "$INFERCLANG_LOG_FILE" 2>&1 + # run clang and pipe its output to InferClang/InferLLVM, or flush it in case the latter crashes + "${CLANG_CMD[@]}" | ("${INFER_FRONTEND_CMD[@]}" || { EC=$?; cat > /dev/null; exit $EC; }) >> "$INFER_FRONTEND_LOG_FILE" 2>&1 STATUSES=("${PIPESTATUS[@]}") STATUS="${STATUSES[0]}" - INFERCLANG_STATUS="${STATUSES[1]}" + INFER_STATUS="${STATUSES[1]}" # if clang fails, then fail, otherwise, fail with the frontend's exitcode if required if [ "$STATUS" == 0 ] && [ -n "$REPORT_FRONTEND_FAILURE" ]; then - STATUS="$INFERCLANG_STATUS" + STATUS="$INFER_STATUS" fi else "${CLANG_CMD[@]}" diff --git a/infer/src/llvm/lMain.ml b/infer/src/llvm/lMain.ml index d40b5bfcf..c7f5cfa61 100644 --- a/infer/src/llvm/lMain.ml +++ b/infer/src/llvm/lMain.ml @@ -41,16 +41,18 @@ let store_tenv tenv = Sil.store_tenv_to_file tenv_filename tenv let () = try - if Array.length Sys.argv < 2 then - raise (UsageError ("Missing source file as first command line argument.")) - else - let filename = Sys.argv.(1) in - let source_file = DB.abs_source_file_from_path filename in - let () = init_global_state source_file in - let lexbuf = Lexing.from_channel (open_in filename) in - let prog = LParser.program LLexer.token lexbuf in - (* let pretty = LPretty.pretty_prog prog in *) - let (cfg, cg, tenv) = LTrans.trans_program prog in - store_icfg tenv cg cfg source_file; store_tenv tenv + let (input, filename) = + if Array.length Sys.argv < 2 then + (stdin, "stdin") (* need a file name for output files *) + else + let fname = Sys.argv.(1) in + (open_in fname, fname) + in + let source_file = DB.abs_source_file_from_path filename in + let () = init_global_state source_file in + let lexbuf = Lexing.from_channel input in + let prog = LParser.program LLexer.token lexbuf in + let (cfg, cg, tenv) = LTrans.trans_program prog in + store_icfg tenv cg cfg source_file; store_tenv tenv with | UsageError msg -> print_string ("Usage error: " ^ msg ^ "\n")