From 4e821129a49e62878fdc541effac9f5ab121694e Mon Sep 17 00:00:00 2001 From: Daiva Naudziuniene <daivan@fb.com> Date: Mon, 14 May 2018 06:20:39 -0700 Subject: [PATCH] [cfg] Log if the cfg is broken for a join node or not Reviewed By: dulmarod Differential Revision: D7985793 fbshipit-source-id: 4010954 --- infer/src/IR/Cfg.ml | 33 +++++++++++++++++++++++-------- infer/src/IR/Cfg.mli | 2 +- infer/src/clang/ClangLogging.ml | 7 +++++-- infer/src/clang/ClangLogging.mli | 3 ++- infer/src/clang/cFrontend_decl.ml | 16 +++++++++------ 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/infer/src/IR/Cfg.ml b/infer/src/IR/Cfg.ml index d4333002d..eb5021fc5 100644 --- a/infer/src/IR/Cfg.ml +++ b/infer/src/IR/Cfg.ml @@ -53,7 +53,7 @@ let iter_all_nodes ?(sorted= false) cfg ~f = |> List.iter ~f:(fun (_, d, n) -> f d n) -let is_proc_cfg_connected proc_desc = +let proc_cfg_broken_for_node proc_desc = let is_exit_node n = match Procdesc.Node.get_kind n with Procdesc.Node.Exit_node _ -> true | _ -> false in @@ -79,26 +79,43 @@ let is_proc_cfg_connected proc_desc = | _ -> is_between_join_and_exit_node n in - let is_broken_node n = + let find_broken_node n = let succs = Procdesc.Node.get_succs n in let preds = Procdesc.Node.get_preds n in match Procdesc.Node.get_kind n with | Procdesc.Node.Start_node _ -> - List.is_empty succs || not (List.is_empty preds) + if List.is_empty succs || not (List.is_empty preds) then Some `Other else None | Procdesc.Node.Exit_node _ -> - not (List.is_empty succs) || List.is_empty preds + if not (List.is_empty succs) || List.is_empty preds then Some `Other else None | Procdesc.Node.Stmt_node _ | Procdesc.Node.Prune_node _ | Procdesc.Node.Skip_node _ -> - List.is_empty succs || List.is_empty preds + if List.is_empty succs || List.is_empty preds then Some `Other else None | Procdesc.Node.Join_node -> (* Join node has the exception that it may be without predecessors and pointing to between_join_and_exit which points to an exit node. This happens when the if branches end with a return. Nested if statements, where all branches have return statements, introduce a sequence of join nodes *) - (List.is_empty preds && not (is_consecutive_join_nodes n Procdesc.NodeSet.empty)) - || (not (List.is_empty preds) && List.is_empty succs) + if + (List.is_empty preds && not (is_consecutive_join_nodes n Procdesc.NodeSet.empty)) + || (not (List.is_empty preds) && List.is_empty succs) + then Some `Join + else None in - not (List.exists ~f:is_broken_node (Procdesc.get_nodes proc_desc)) + let rec find_first_broken nodes res_broken_node = + match nodes with + | [] -> + res_broken_node + | n :: nodes' -> + let broken_node = find_broken_node n in + match broken_node with + | None -> + find_first_broken nodes' res_broken_node + | Some `Join -> + find_first_broken nodes' broken_node + | Some `Other -> + broken_node + in + find_first_broken (Procdesc.get_nodes proc_desc) None let load_statement = diff --git a/infer/src/IR/Cfg.mli b/infer/src/IR/Cfg.mli index 0b8216e95..4d1781676 100644 --- a/infer/src/IR/Cfg.mli +++ b/infer/src/IR/Cfg.mli @@ -32,7 +32,7 @@ val create_proc_desc : t -> ProcAttributes.t -> Procdesc.t val iter_all_nodes : ?sorted:bool -> t -> f:(Procdesc.t -> Procdesc.Node.t -> unit) -> unit (** Iterate over all the nodes in the cfg *) -val is_proc_cfg_connected : Procdesc.t -> bool +val proc_cfg_broken_for_node : Procdesc.t -> [`Join | `Other] option (** checks whether a cfg for the given procdesc is connected or not *) val save_attributes : SourceFile.t -> t -> unit diff --git a/infer/src/clang/ClangLogging.ml b/infer/src/clang/ClangLogging.ml index 137662d36..310e93efa 100644 --- a/infer/src/clang/ClangLogging.ml +++ b/infer/src/clang/ClangLogging.ml @@ -22,15 +22,18 @@ let log_caught_exception (trans_unit_ctx: CFrontend_config.translation_unit_cont EventLogger.log caught_exception -let log_broken_cfg procdesc exception_triggered_location ~lang = +let log_broken_cfg ~broken_node procdesc exception_triggered_location ~lang = let proc_location = Procdesc.get_loc procdesc in + let exception_type = + match broken_node with `Other -> "Broken CFG" | `Join -> "Broken CFG at join node" + in let cfg_exception = EventLogger.FrontendException { source_location_start= proc_location ; source_location_end= proc_location ; ast_node= None ; exception_triggered_location - ; exception_type= "Broken CFG" + ; exception_type ; lang } in EventLogger.log cfg_exception diff --git a/infer/src/clang/ClangLogging.mli b/infer/src/clang/ClangLogging.mli index 90774b49d..6ab4387de 100644 --- a/infer/src/clang/ClangLogging.mli +++ b/infer/src/clang/ClangLogging.mli @@ -13,4 +13,5 @@ val log_caught_exception : CFrontend_config.translation_unit_context -> string -> Logging.ocaml_pos -> Clang_ast_t.source_location * Clang_ast_t.source_location -> string option -> unit -val log_broken_cfg : Procdesc.t -> Logging.ocaml_pos -> lang:string -> unit +val log_broken_cfg : + broken_node:[`Join | `Other] -> Procdesc.t -> Logging.ocaml_pos -> lang:string -> unit diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index 559f8256f..db067f88f 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -62,7 +62,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron in let f () = match Typ.Procname.Hash.find cfg procname with - | procdesc when Procdesc.is_defined procdesc && not (model_exists procname) -> + | procdesc when Procdesc.is_defined procdesc && not (model_exists procname) + -> ( let vars_to_destroy = CTrans_utils.Scope.compute_vars_to_destroy body in let context = CContext.create_context trans_unit_ctx tenv cfg procdesc class_decl_opt @@ -80,11 +81,14 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron Procdesc.Node.add_locals_ret_declaration start_node proc_attributes (Procdesc.get_locals procdesc) ; Procdesc.node_set_succs_exn procdesc start_node meth_body_nodes [] ; - if not (Cfg.is_proc_cfg_connected procdesc) then - let lang = - CFrontend_config.string_of_clang_lang trans_unit_ctx.CFrontend_config.lang - in - ClangLogging.log_broken_cfg procdesc __POS__ ~lang + match Cfg.proc_cfg_broken_for_node procdesc with + | None -> + () + | Some broken_node -> + let lang = + CFrontend_config.string_of_clang_lang trans_unit_ctx.CFrontend_config.lang + in + ClangLogging.log_broken_cfg ~broken_node procdesc __POS__ ~lang ) | _ -> () | exception Caml.Not_found ->