From 2ede6c1ac4c4b9ce8a7697676eedd344865b1426 Mon Sep 17 00:00:00 2001 From: Varun Arora Date: Fri, 19 Jan 2018 09:56:38 -0800 Subject: [PATCH] [clang] Added logging of caught exceptions of type Unimplemented to clang frontend Summary: - During program translation, infer logs details about Unimplemented exceptions that are caught - Exception type, source location, exception triggering location, and the ast_item it occurred in - Logging is integrated with EventLogger library - New type of event in EventLogger - Unimplemented exception type extended to add support for storing these extra details - All instances where unimplemented exception is raised modified to pass these details Reviewed By: dulmarod Differential Revision: D6748734 fbshipit-source-id: 725c7f3 --- infer/src/base/EventLogger.ml | 41 ++++++++++++++++++++++++++++ infer/src/base/EventLogger.mli | 10 +++++++ infer/src/clang/cFrontend_config.ml | 19 +++++++------ infer/src/clang/cFrontend_config.mli | 19 +++++++------ infer/src/clang/cFrontend_decl.ml | 24 +++++++++++++--- infer/src/clang/cTrans.ml | 40 ++++++++++++++++----------- 6 files changed, 117 insertions(+), 36 deletions(-) diff --git a/infer/src/base/EventLogger.ml b/infer/src/base/EventLogger.ml index b63e2035d..acddffc91 100644 --- a/infer/src/base/EventLogger.ml +++ b/infer/src/base/EventLogger.ml @@ -74,6 +74,42 @@ end let get_log_identifier () = Random_id.get () +let bind_default opt map_func prev = match opt with Some x -> map_func x prev | None -> prev + +type frontend_exception = + { exception_type: string + ; source_location_start: Location.t + ; source_location_end: Location.t + ; exception_file: string + ; exception_line: int + ; ast_node: string option + ; lang: string } + +let create_frontend_exception_row base record = + let open JsonBuilder in + base |> add_string ~key:"exception_type" ~data:record.exception_type + |> add_string ~key:"source_location_start_file" + ~data:(SourceFile.to_rel_path record.source_location_start.file) + |> add_string ~key:"source_location_start_pos" + ~data: + (String.concat + [ string_of_int record.source_location_start.line + ; ":" + ; string_of_int record.source_location_start.col ]) + |> add_string ~key:"source_location_end_file" + ~data:(SourceFile.to_rel_path record.source_location_end.file) + |> add_string ~key:"source_location_end_pos" + ~data: + (String.concat + [ string_of_int record.source_location_end.line + ; ":" + ; string_of_int record.source_location_end.col ]) + |> add_string ~key:"exception_triggered_location" + ~data:(String.concat [record.exception_file; ":"; string_of_int record.exception_line]) + |> bind_default record.ast_node (fun ast_node -> add_string ~key:"ast_node" ~data:ast_node) + |> add_string ~key:"lang" ~data:record.lang + + type procedures_translated = { procedures_translated_total: int ; procedures_translated_failed: int @@ -90,12 +126,15 @@ let create_procedures_translated_row base record = type event = | UncaughtException of exn * int + | FrontendException of frontend_exception | ProceduresTranslatedSummary of procedures_translated let string_of_event event = match event with | UncaughtException _ -> "UncaughtException" + | FrontendException _ -> + "FrontendException" | ProceduresTranslatedSummary _ -> "ProceduresTranslatedSummary" @@ -131,6 +170,8 @@ let create_row event = base |> add_string ~key:"exception" ~data:(Caml.Printexc.exn_slot_name exn) |> add_string ~key:"exception_info" ~data:(Exn.to_string exn) |> add_int ~key:"exitcode" ~data:exitcode + | FrontendException record -> + create_frontend_exception_row base record | ProceduresTranslatedSummary record -> create_procedures_translated_row base record ) |> JsonBuilder.to_json diff --git a/infer/src/base/EventLogger.mli b/infer/src/base/EventLogger.mli index d3b5ffe07..e8fa5d726 100644 --- a/infer/src/base/EventLogger.mli +++ b/infer/src/base/EventLogger.mli @@ -7,6 +7,15 @@ * of patent rights can be found in the PATENTS file in the same directory. *) +type frontend_exception = + { exception_type: string + ; source_location_start: Location.t + ; source_location_end: Location.t + ; exception_file: string + ; exception_line: int + ; ast_node: string option + ; lang: string } + type procedures_translated = { procedures_translated_total: int ; procedures_translated_failed: int @@ -15,6 +24,7 @@ type procedures_translated = type event = | UncaughtException of exn * int (** exception, exitcode *) + | FrontendException of frontend_exception (** record of caught exception *) | ProceduresTranslatedSummary of procedures_translated (** record of procedures translated *) val get_log_identifier : unit -> string diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index 786e4248f..44573b1a3 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -12,14 +12,6 @@ module F = Format (** Module that contains constants and global state used in the frontend *) -exception IncorrectAssumption of string - -let incorrect_assumption fmt = F.kasprintf (fun msg -> raise (IncorrectAssumption msg)) fmt - -exception Unimplemented of string - -let unimplemented fmt = F.kasprintf (fun msg -> raise (Unimplemented msg)) fmt - type clang_lang = C | CPP | ObjC | ObjCPP [@@deriving compare] let string_of_clang_lang (lang: clang_lang) : string = @@ -28,6 +20,17 @@ let string_of_clang_lang (lang: clang_lang) : string = let equal_clang_lang = [%compare.equal : clang_lang] +exception IncorrectAssumption of string + +let incorrect_assumption fmt = F.kasprintf (fun msg -> raise (IncorrectAssumption msg)) fmt + +exception Unimplemented of + string * (string * int * int * int) * Clang_ast_t.source_range * string option + +let unimplemented pos source_range ast_node fmt = + F.kasprintf (fun msg -> raise (Unimplemented (msg, pos, source_range, ast_node))) fmt + + type translation_unit_context = {lang: clang_lang; source_file: SourceFile.t} (** Constants *) diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index 7ebaece2c..d7788ea1a 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -11,24 +11,27 @@ open! IStd (** Module that contains constants and global state used in the frontend *) +type clang_lang = C | CPP | ObjC | ObjCPP [@@deriving compare] + +val string_of_clang_lang : clang_lang -> string + +val equal_clang_lang : clang_lang -> clang_lang -> bool + exception IncorrectAssumption of string val incorrect_assumption : ('a, Format.formatter, unit, _) format4 -> 'a (** Used to mark places in the frontend that incorrectly assume something to be impossible. TODO(t21762295) get rid of all instances of this. *) -exception Unimplemented of string +exception Unimplemented of + string * (string * int * int * int) * Clang_ast_t.source_range * string option -val unimplemented : ('a, Format.formatter, unit, _) format4 -> 'a +val unimplemented : + string * int * int * int -> Clang_ast_t.source_range -> string option + -> ('a, Format.formatter, unit, _) format4 -> 'a (** Raise Unimplemented. This is caught at the level of translating a method and makes the frontend give up on that method. *) -type clang_lang = C | CPP | ObjC | ObjCPP [@@deriving compare] - -val string_of_clang_lang : clang_lang -> string - -val equal_clang_lang : clang_lang -> clang_lang -> bool - type translation_unit_context = {lang: clang_lang; source_file: SourceFile.t} (** Constants *) diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index 18cc76fc6..87a03d39f 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -14,17 +14,32 @@ module F = Format module L = Logging -let protect ~f ~recover ~pp_context = +let protect ~f ~recover ~pp_context (trans_unit_ctx: CFrontend_config.translation_unit_context) = let log_and_recover ~print fmt = recover () ; incr CFrontend_config.procedures_failed ; (if print then L.internal_error else L.(debug Capture Quiet)) ("%a@\n" ^^ fmt) pp_context () in + let caught_exception exception_type (exception_file, exception_line, _, _) + (source_location_start, source_location_end) ast_node = + EventLogger.FrontendException + { exception_type + ; source_location_start= CLocation.clang_to_sil_location trans_unit_ctx source_location_start + ; source_location_end= CLocation.clang_to_sil_location trans_unit_ctx source_location_end + ; exception_file + ; exception_line + ; ast_node + ; lang= CFrontend_config.string_of_clang_lang trans_unit_ctx.lang } + in try f () with (* Always keep going in case of known limitations of the frontend, crash otherwise (by not catching the exception) unless `--keep-going` was passed. Print errors we should fix (t21762295) to the console. *) - | CFrontend_config.Unimplemented msg -> + | CFrontend_config.Unimplemented (msg, exception_pos, source_range, ast_node) -> + let caught_unimplemented_exception = + caught_exception "Unimplemented" exception_pos source_range ast_node + in + EventLogger.log caught_unimplemented_exception ; log_and_recover ~print:false "Unimplemented feature:@\n %s@\n" msg | CFrontend_config.IncorrectAssumption msg -> (* FIXME(t21762295): we do not expect this to happen but it does *) @@ -83,7 +98,7 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron | exception Not_found -> () in - protect ~f ~recover ~pp_context + protect ~f ~recover ~pp_context trans_unit_ctx let function_decl trans_unit_ctx tenv cfg cg func_decl block_data_opt = @@ -368,7 +383,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron ~pp_context:(fun fmt () -> F.fprintf fmt "Error adding types from decl '%a'" (Pp.to_string ~f:Clang_ast_j.string_of_decl) - dec ) ; + dec ) + trans_unit_ctx ; List.iter ~f:translate method_decls | _ -> () ) ; diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index b808cf9e4..22cf00ac9 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -393,12 +393,14 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let exps = [(exp, typ)] in {empty_res_trans with exps; instrs} | Tfun _ | Tvoid | Tarray _ | TVar _ -> - CFrontend_config.unimplemented "fill_typ_with_zero on type %a" (Typ.pp Pp.text) typ + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None + "fill_typ_with_zero on type %a" (Typ.pp Pp.text) typ in let res_trans = fill_typ_with_zero var_exp_typ in {res_trans with initd_exps= [fst var_exp_typ]} | None -> - CFrontend_config.unimplemented "Retrieving var from non-InitListExpr parent" + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None + "Retrieving var from non-InitListExpr parent" let no_op_trans succ_nodes = {empty_res_trans with root_nodes= succ_nodes} @@ -746,7 +748,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | `CXXMethod | `CXXConversion | `CXXConstructor | `CXXDestructor -> method_deref_trans trans_state pre_trans_result decl_ref stmt_info decl_kind | _ -> - CFrontend_config.unimplemented + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None "Decl ref expression %a with pointer %d still needs to be translated" (Pp.to_string ~f:Clang_ast_j.string_of_decl_kind) decl_kind decl_ref.Clang_ast_t.dr_decl_pointer @@ -1518,7 +1520,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let root_nodes' = if root_nodes <> [] then root_nodes else op_res_trans.root_nodes in {op_res_trans with root_nodes= root_nodes'} | _ -> - CFrontend_config.unimplemented "BinaryConditionalOperator not translated" + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None + "BinaryConditionalOperator not translated" (* Translate a condition for if/loops statement. It shorts-circuit and/or. *) @@ -2069,7 +2072,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | [stmt] -> [init_expr_trans trans_state (var_exp, var_typ) stmt_info (Some stmt)] | _ -> - CFrontend_config.unimplemented + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None "InitListExpression for var %a type %a with multiple init statements" Exp.pp var_exp (Typ.pp_full Pp.text) var_typ @@ -2115,8 +2118,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | Tint _ | Tfloat _ | Tptr _ -> initListExpr_builtin_trans trans_state_pri init_stmt_info stmts var_exp var_typ | _ -> - CFrontend_config.unimplemented "InitListExp for var %a of type %a" Exp.pp var_exp - (Typ.pp Pp.text) var_typ + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None + "InitListExp for var %a of type %a" Exp.pp var_exp (Typ.pp Pp.text) var_typ in let nname = "InitListExp" in let res_trans = @@ -2257,8 +2260,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | (TypedefDecl _ | TypeAliasDecl _ | UsingDecl _ | UsingDirectiveDecl _) :: _ -> empty_res_trans | decl :: _ -> - CFrontend_config.unimplemented "In DeclStmt found an unknown declaration type %s" - (Clang_ast_j.string_of_decl decl) + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None + "In DeclStmt found an unknown declaration type %s" (Clang_ast_j.string_of_decl decl) | [] -> assert false in @@ -2951,7 +2954,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s binaryOperator_trans trans_state binop_info stmt_info expr_info stmt_list - and attributedStmt_trans trans_state stmts attrs = + and attributedStmt_trans trans_state stmt_info stmts attrs = let open Clang_ast_t in match (stmts, attrs) with | [stmt], [attr] -> ( @@ -2959,12 +2962,13 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | NullStmt _, FallThroughAttr _ -> no_op_trans trans_state.succ_nodes | _ -> - CFrontend_config.unimplemented + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None "attributedStmt [stmt] [attr] with:@\nstmt=%s@\nattr=%s@\n" (Clang_ast_j.string_of_stmt stmt) (Clang_ast_j.string_of_attribute attr) ) | _ -> - CFrontend_config.unimplemented "attributedStmt with:@\nstmts=[%a]@\nattrs=[%a]@\n" + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None + "attributedStmt with:@\nstmts=[%a]@\nattrs=[%a]@\n" (Pp.semicolon_seq (Pp.to_string ~f:Clang_ast_j.string_of_stmt)) stmts (Pp.semicolon_seq (Pp.to_string ~f:Clang_ast_j.string_of_attribute)) @@ -3207,8 +3211,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | LambdaExpr (stmt_info, _, expr_info, lambda_expr_info) -> let trans_state' = {trans_state with priority= Free} in lambdaExpr_trans trans_state' stmt_info expr_info lambda_expr_info - | AttributedStmt (_, stmts, attrs) -> - attributedStmt_trans trans_state stmts attrs + | AttributedStmt (stmt_info, stmts, attrs) -> + attributedStmt_trans trans_state stmt_info stmts attrs | TypeTraitExpr (_, _, expr_info, type_trait_info) -> booleanValue_trans trans_state expr_info type_trait_info.Clang_ast_t.xtti_value | CXXNoexceptExpr (_, _, expr_info, cxx_noexcept_expr_info) -> @@ -3230,7 +3234,9 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | SubstNonTypeTemplateParmExpr _ | SubstNonTypeTemplateParmPackExpr _ | CXXDependentScopeMemberExpr _ -> - CFrontend_config.unimplemented "Translation of templated code is unsupported: %a" + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range + (Some (Clang_ast_proj.get_stmt_kind_string instr)) + "Translation of templated code is unsupported: %a" (Pp.to_string ~f:Clang_ast_j.string_of_stmt) instr | ForStmt (_, _) | WhileStmt (_, _) | DoStmt (_, _) | ObjCForCollectionStmt (_, _) -> @@ -3328,7 +3334,9 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s | SEHLeaveStmt _ | SEHTryStmt _ | DefaultStmt _ -> - CFrontend_config.unimplemented "Statement translation for kind %s: %a" + CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range + (Some (Clang_ast_proj.get_stmt_kind_string instr)) + "Statement translation for kind %s: %a" (Clang_ast_proj.get_stmt_kind_string instr) (Pp.to_string ~f:Clang_ast_j.string_of_stmt) instr