[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
master
Varun Arora 7 years ago committed by Facebook Github Bot
parent 360151eb10
commit 2ede6c1ac4

@ -74,6 +74,42 @@ end
let get_log_identifier () = Random_id.get () 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 = type procedures_translated =
{ procedures_translated_total: int { procedures_translated_total: int
; procedures_translated_failed: int ; procedures_translated_failed: int
@ -90,12 +126,15 @@ let create_procedures_translated_row base record =
type event = type event =
| UncaughtException of exn * int | UncaughtException of exn * int
| FrontendException of frontend_exception
| ProceduresTranslatedSummary of procedures_translated | ProceduresTranslatedSummary of procedures_translated
let string_of_event event = let string_of_event event =
match event with match event with
| UncaughtException _ -> | UncaughtException _ ->
"UncaughtException" "UncaughtException"
| FrontendException _ ->
"FrontendException"
| ProceduresTranslatedSummary _ -> | ProceduresTranslatedSummary _ ->
"ProceduresTranslatedSummary" "ProceduresTranslatedSummary"
@ -131,6 +170,8 @@ let create_row event =
base |> add_string ~key:"exception" ~data:(Caml.Printexc.exn_slot_name exn) base |> add_string ~key:"exception" ~data:(Caml.Printexc.exn_slot_name exn)
|> add_string ~key:"exception_info" ~data:(Exn.to_string exn) |> add_string ~key:"exception_info" ~data:(Exn.to_string exn)
|> add_int ~key:"exitcode" ~data:exitcode |> add_int ~key:"exitcode" ~data:exitcode
| FrontendException record ->
create_frontend_exception_row base record
| ProceduresTranslatedSummary record -> | ProceduresTranslatedSummary record ->
create_procedures_translated_row base record ) create_procedures_translated_row base record )
|> JsonBuilder.to_json |> JsonBuilder.to_json

@ -7,6 +7,15 @@
* of patent rights can be found in the PATENTS file in the same directory. * 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 = type procedures_translated =
{ procedures_translated_total: int { procedures_translated_total: int
; procedures_translated_failed: int ; procedures_translated_failed: int
@ -15,6 +24,7 @@ type procedures_translated =
type event = type event =
| UncaughtException of exn * int (** exception, exitcode *) | UncaughtException of exn * int (** exception, exitcode *)
| FrontendException of frontend_exception (** record of caught exception *)
| ProceduresTranslatedSummary of procedures_translated (** record of procedures translated *) | ProceduresTranslatedSummary of procedures_translated (** record of procedures translated *)
val get_log_identifier : unit -> string val get_log_identifier : unit -> string

@ -12,14 +12,6 @@ module F = Format
(** Module that contains constants and global state used in the frontend *) (** 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] type clang_lang = C | CPP | ObjC | ObjCPP [@@deriving compare]
let string_of_clang_lang (lang: clang_lang) : string = 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] 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} type translation_unit_context = {lang: clang_lang; source_file: SourceFile.t}
(** Constants *) (** Constants *)

@ -11,24 +11,27 @@ open! IStd
(** Module that contains constants and global state used in the frontend *) (** 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 exception IncorrectAssumption of string
val incorrect_assumption : ('a, Format.formatter, unit, _) format4 -> 'a val incorrect_assumption : ('a, Format.formatter, unit, _) format4 -> 'a
(** Used to mark places in the frontend that incorrectly assume something to be (** Used to mark places in the frontend that incorrectly assume something to be
impossible. TODO(t21762295) get rid of all instances of this. *) 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 (** Raise Unimplemented. This is caught at the level of translating a method and makes the frontend
give up on that method. *) 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} type translation_unit_context = {lang: clang_lang; source_file: SourceFile.t}
(** Constants *) (** Constants *)

@ -14,17 +14,32 @@ module F = Format
module L = Logging 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 = let log_and_recover ~print fmt =
recover () ; recover () ;
incr CFrontend_config.procedures_failed ; incr CFrontend_config.procedures_failed ;
(if print then L.internal_error else L.(debug Capture Quiet)) ("%a@\n" ^^ fmt) pp_context () (if print then L.internal_error else L.(debug Capture Quiet)) ("%a@\n" ^^ fmt) pp_context ()
in 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 try f () with
(* Always keep going in case of known limitations of the frontend, crash otherwise (by not (* 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 catching the exception) unless `--keep-going` was passed. Print errors we should fix
(t21762295) to the console. *) (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 log_and_recover ~print:false "Unimplemented feature:@\n %s@\n" msg
| CFrontend_config.IncorrectAssumption msg -> | CFrontend_config.IncorrectAssumption msg ->
(* FIXME(t21762295): we do not expect this to happen but it does *) (* 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 -> | exception Not_found ->
() ()
in 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 = 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 () -> ~pp_context:(fun fmt () ->
F.fprintf fmt "Error adding types from decl '%a'" F.fprintf fmt "Error adding types from decl '%a'"
(Pp.to_string ~f:Clang_ast_j.string_of_decl) (Pp.to_string ~f:Clang_ast_j.string_of_decl)
dec ) ; dec )
trans_unit_ctx ;
List.iter ~f:translate method_decls List.iter ~f:translate method_decls
| _ -> | _ ->
() ) ; () ) ;

@ -393,12 +393,14 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
let exps = [(exp, typ)] in let exps = [(exp, typ)] in
{empty_res_trans with exps; instrs} {empty_res_trans with exps; instrs}
| Tfun _ | Tvoid | Tarray _ | TVar _ -> | 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 in
let res_trans = fill_typ_with_zero var_exp_typ in let res_trans = fill_typ_with_zero var_exp_typ in
{res_trans with initd_exps= [fst var_exp_typ]} {res_trans with initd_exps= [fst var_exp_typ]}
| None -> | 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} 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 -> | `CXXMethod | `CXXConversion | `CXXConstructor | `CXXDestructor ->
method_deref_trans trans_state pre_trans_result decl_ref stmt_info decl_kind 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" "Decl ref expression %a with pointer %d still needs to be translated"
(Pp.to_string ~f:Clang_ast_j.string_of_decl_kind) (Pp.to_string ~f:Clang_ast_j.string_of_decl_kind)
decl_kind decl_ref.Clang_ast_t.dr_decl_pointer 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 let root_nodes' = if root_nodes <> [] then root_nodes else op_res_trans.root_nodes in
{op_res_trans with root_nodes= root_nodes'} {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. *) (* 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] -> | [stmt] ->
[init_expr_trans trans_state (var_exp, var_typ) stmt_info (Some 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 "InitListExpression for var %a type %a with multiple init statements" Exp.pp var_exp
(Typ.pp_full Pp.text) var_typ (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 _ -> | Tint _ | Tfloat _ | Tptr _ ->
initListExpr_builtin_trans trans_state_pri init_stmt_info stmts var_exp var_typ 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 CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None
(Typ.pp Pp.text) var_typ "InitListExp for var %a of type %a" Exp.pp var_exp (Typ.pp Pp.text) var_typ
in in
let nname = "InitListExp" in let nname = "InitListExp" in
let res_trans = let res_trans =
@ -2257,8 +2260,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
| (TypedefDecl _ | TypeAliasDecl _ | UsingDecl _ | UsingDirectiveDecl _) :: _ -> | (TypedefDecl _ | TypeAliasDecl _ | UsingDecl _ | UsingDirectiveDecl _) :: _ ->
empty_res_trans empty_res_trans
| decl :: _ -> | decl :: _ ->
CFrontend_config.unimplemented "In DeclStmt found an unknown declaration type %s" CFrontend_config.unimplemented __POS__ stmt_info.Clang_ast_t.si_source_range None
(Clang_ast_j.string_of_decl decl) "In DeclStmt found an unknown declaration type %s" (Clang_ast_j.string_of_decl decl)
| [] -> | [] ->
assert false assert false
in 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 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 let open Clang_ast_t in
match (stmts, attrs) with match (stmts, attrs) with
| [stmt], [attr] -> ( | [stmt], [attr] -> (
@ -2959,12 +2962,13 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
| NullStmt _, FallThroughAttr _ -> | NullStmt _, FallThroughAttr _ ->
no_op_trans trans_state.succ_nodes 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" "attributedStmt [stmt] [attr] with:@\nstmt=%s@\nattr=%s@\n"
(Clang_ast_j.string_of_stmt stmt) (Clang_ast_j.string_of_stmt stmt)
(Clang_ast_j.string_of_attribute attr) ) (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)) (Pp.semicolon_seq (Pp.to_string ~f:Clang_ast_j.string_of_stmt))
stmts stmts
(Pp.semicolon_seq (Pp.to_string ~f:Clang_ast_j.string_of_attribute)) (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) -> | LambdaExpr (stmt_info, _, expr_info, lambda_expr_info) ->
let trans_state' = {trans_state with priority= Free} in let trans_state' = {trans_state with priority= Free} in
lambdaExpr_trans trans_state' stmt_info expr_info lambda_expr_info lambdaExpr_trans trans_state' stmt_info expr_info lambda_expr_info
| AttributedStmt (_, stmts, attrs) -> | AttributedStmt (stmt_info, stmts, attrs) ->
attributedStmt_trans trans_state stmts attrs attributedStmt_trans trans_state stmt_info stmts attrs
| TypeTraitExpr (_, _, expr_info, type_trait_info) -> | TypeTraitExpr (_, _, expr_info, type_trait_info) ->
booleanValue_trans trans_state expr_info type_trait_info.Clang_ast_t.xtti_value booleanValue_trans trans_state expr_info type_trait_info.Clang_ast_t.xtti_value
| CXXNoexceptExpr (_, _, expr_info, cxx_noexcept_expr_info) -> | CXXNoexceptExpr (_, _, expr_info, cxx_noexcept_expr_info) ->
@ -3230,7 +3234,9 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
| SubstNonTypeTemplateParmExpr _ | SubstNonTypeTemplateParmExpr _
| SubstNonTypeTemplateParmPackExpr _ | SubstNonTypeTemplateParmPackExpr _
| CXXDependentScopeMemberExpr _ -> | 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) (Pp.to_string ~f:Clang_ast_j.string_of_stmt)
instr instr
| ForStmt (_, _) | WhileStmt (_, _) | DoStmt (_, _) | ObjCForCollectionStmt (_, _) -> | ForStmt (_, _) | WhileStmt (_, _) | DoStmt (_, _) | ObjCForCollectionStmt (_, _) ->
@ -3328,7 +3334,9 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
| SEHLeaveStmt _ | SEHLeaveStmt _
| SEHTryStmt _ | SEHTryStmt _
| DefaultStmt _ -> | 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) (Clang_ast_proj.get_stmt_kind_string instr)
(Pp.to_string ~f:Clang_ast_j.string_of_stmt) (Pp.to_string ~f:Clang_ast_j.string_of_stmt)
instr instr

Loading…
Cancel
Save