Refactor InferPrint and store the lint issues in a serialized data structure that InferPrint picks up and prints.

Reviewed By: jberdine

Differential Revision: D3641944

fbshipit-source-id: aad36e6
master
Dulma Churchill 8 years ago committed by Facebook Github Bot 6
parent 121c968e77
commit 3d39fba622

@ -129,6 +129,7 @@ let inferconfig_file = ".inferconfig"
let ivar_attributes = "ivar_attributes" let ivar_attributes = "ivar_attributes"
let lint_issues_dir_name = "lint_issues"
let linters_mode_enabled = false let linters_mode_enabled = false
(** letters used in the analysis output *) (** letters used in the analysis output *)

@ -80,6 +80,7 @@ val idempotent_getters : bool
val incremental_procs : bool val incremental_procs : bool
val initial_analysis_time : float val initial_analysis_time : float
val ivar_attributes : string val ivar_attributes : string
val lint_issues_dir_name : string
val linters_mode_enabled : bool val linters_mode_enabled : bool
val load_average : float option val load_average : float option
val log_analysis_crash : string val log_analysis_crash : string

@ -167,7 +167,6 @@ let update errlog_old errlog_new =
(fun (ekind, infp, s, desc, severity) l -> (fun (ekind, infp, s, desc, severity) l ->
ignore (add_issue errlog_old (ekind, infp, s, desc, severity) l)) errlog_new ignore (add_issue errlog_old (ekind, infp, s, desc, severity) l)) errlog_new
let log_issue _ekind err_log loc node_id_key session ltr pre_opt exn = let log_issue _ekind err_log loc node_id_key session ltr pre_opt exn =
let err_name, desc, ml_loc_opt, visibility, severity, force_kind, eclass = let err_name, desc, ml_loc_opt, visibility, severity, force_kind, eclass =
Exceptions.recognize_exception exn in Exceptions.recognize_exception exn in

@ -46,13 +46,6 @@ let begin_latex_file fmt =
let table_of_contents = true in let table_of_contents = true in
Latex.pp_begin fmt (author, title, table_of_contents) Latex.pp_begin fmt (author, title, table_of_contents)
(** Write proc summary to latex file *)
let write_summary_latex fmt summary =
let proc_name = Specs.get_proc_name summary in
Latex.pp_section fmt ("Analysis of function "
^ (Latex.convert_string (Procname.to_string proc_name)));
F.fprintf fmt "@[<v>%a@]" (Specs.pp_summary (pe_latex Black) Config.whole_seconds) summary
let error_desc_to_csv_string error_desc = let error_desc_to_csv_string error_desc =
let pp fmt () = F.fprintf fmt "%a" Localise.pp_error_desc error_desc in let pp fmt () = F.fprintf fmt "%a" Localise.pp_error_desc error_desc in
Escape.escape_csv (pp_to_string pp ()) Escape.escape_csv (pp_to_string pp ())
@ -220,7 +213,7 @@ module ProcsCsv = struct
Io_infer.Xml.tag_proof_trace Io_infer.Xml.tag_proof_trace
(** Write proc summary stats in csv format *) (** Write proc summary stats in csv format *)
let pp_summary top_proc_set fmt summary = let pp_summary fmt top_proc_set summary =
let pp x = F.fprintf fmt x in let pp x = F.fprintf fmt x in
let sv = summary_values top_proc_set summary in let sv = summary_values top_proc_set summary in
pp "\"%s\"," (Escape.escape_csv sv.vname); pp "\"%s\"," (Escape.escape_csv sv.vname);
@ -247,7 +240,7 @@ module ProcsXml = struct
let xml_procs_id = ref 0 let xml_procs_id = ref 0
(** print proc in xml *) (** print proc in xml *)
let pp_proc top_proc_set fmt summary = let pp_proc fmt top_proc_set summary =
let sv = summary_values top_proc_set summary in let sv = summary_values top_proc_set summary in
let subtree label contents = let subtree label contents =
Io_infer.Xml.create_tree label [] [(Io_infer.Xml.String contents)] in Io_infer.Xml.create_tree label [] [(Io_infer.Xml.String contents)] in
@ -289,8 +282,8 @@ module ProcsXml = struct
end end
module BugsCsv = struct module IssuesCsv = struct
let csv_bugs_id = ref 0 let csv_issues_id = ref 0
let pp_header fmt () = let pp_header fmt () =
Format.fprintf fmt "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s@\n" Format.fprintf fmt "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s@\n"
@ -312,11 +305,14 @@ module BugsCsv = struct
"advice" "advice"
(** Write bug report in csv format *) (** Write bug report in csv format *)
let pp_bugs error_filter fmt summary = let pp_issues_of_error_log fmt error_filter _ file_opt procname err_log =
let pp x = F.fprintf fmt x in let pp x = F.fprintf fmt x in
let err_log = summary.Specs.attributes.ProcAttributes.err_log in
let pp_row (_, node_key) loc _ ekind in_footprint error_name error_desc severity ltr _ eclass = let pp_row (_, node_key) loc _ ekind in_footprint error_name error_desc severity ltr _ eclass =
if in_footprint && error_filter error_desc error_name then let source_file =
match file_opt with
| Some file -> file
| None -> loc.Location.file in
if in_footprint && error_filter source_file error_desc error_name then
let err_desc_string = error_desc_to_csv_string error_desc in let err_desc_string = error_desc_to_csv_string error_desc in
let err_advice_string = error_advice_to_csv_string error_desc in let err_advice_string = error_advice_to_csv_string error_desc in
let qualifier_tag_xml = let qualifier_tag_xml =
@ -328,9 +324,8 @@ module BugsCsv = struct
Escape.escape_csv s in Escape.escape_csv s in
let kind = Exceptions.err_kind_string ekind in let kind = Exceptions.err_kind_string ekind in
let type_str = Localise.to_string error_name in let type_str = Localise.to_string error_name in
let procedure_id = Procname.to_filename (Specs.get_proc_name summary) in let procedure_id = Procname.to_filename procname in
let filename = let filename = DB.source_file_to_string source_file in
DB.source_file_to_string summary.Specs.attributes.ProcAttributes.loc.Location.file in
let always_report = let always_report =
match Localise.error_desc_extract_tag_value error_desc "always_report" with match Localise.error_desc_extract_tag_value error_desc "always_report" with
| "" -> "false" | "" -> "false"
@ -338,27 +333,29 @@ module BugsCsv = struct
let trace = let trace =
Jsonbug_j.string_of_json_trace { trace = loc_trace_to_jsonbug_record ltr ekind } in Jsonbug_j.string_of_json_trace { trace = loc_trace_to_jsonbug_record ltr ekind } in
incr csv_bugs_id; incr csv_issues_id;
pp "%s," (Exceptions.err_class_string eclass); pp "%s," (Exceptions.err_class_string eclass);
pp "%s," kind; pp "%s," kind;
pp "%s," type_str; pp "%s," type_str;
pp "\"%s\"," err_desc_string; pp "\"%s\"," err_desc_string;
pp "%s," severity; pp "%s," severity;
pp "%d," loc.Location.line; pp "%d," loc.Location.line;
pp "\"%s\"," (Escape.escape_csv (Procname.to_string (Specs.get_proc_name summary))); pp "\"%s\"," (Escape.escape_csv (Procname.to_string procname));
pp "\"%s\"," (Escape.escape_csv procedure_id); pp "\"%s\"," (Escape.escape_csv procedure_id);
pp "%s," filename; pp "%s," filename;
pp "\"%s\"," (Escape.escape_csv trace); pp "\"%s\"," (Escape.escape_csv trace);
pp "\"%d\"," node_key; pp "\"%d\"," node_key;
pp "\"%s\"," qualifier_tag_xml; pp "\"%s\"," qualifier_tag_xml;
pp "\"%d\"," (get_bug_hash kind type_str procedure_id filename node_key error_desc); pp "\"%d\"," (get_bug_hash kind type_str procedure_id filename node_key error_desc);
pp "\"%d\"," !csv_bugs_id; (* bug id *) pp "\"%d\"," !csv_issues_id; (* bug id *)
pp "\"%s\"," always_report; pp "\"%s\"," always_report;
pp "\"%s\"@\n" err_advice_string; in pp "\"%s\"@\n" err_advice_string; in
Errlog.iter pp_row err_log Errlog.iter pp_row err_log
end end
module BugsJson = struct module IssuesJson = struct
let is_first_item = ref true let is_first_item = ref true
let pp_json_open fmt () = F.fprintf fmt "[@?" let pp_json_open fmt () = F.fprintf fmt "[@?"
let pp_json_close fmt () = F.fprintf fmt "]\n@?" let pp_json_close fmt () = F.fprintf fmt "]\n@?"
@ -373,18 +370,20 @@ module BugsJson = struct
else file else file
(** Write bug report in JSON format *) (** Write bug report in JSON format *)
let pp_bugs error_filter fmt summary = let pp_issues_of_error_log fmt error_filter _ file_opt procname err_log =
let pp x = F.fprintf fmt x in let pp x = F.fprintf fmt x in
let err_log = summary.Specs.attributes.ProcAttributes.err_log in
let pp_row let pp_row
(_, node_key) loc ml_loc_opt ekind in_footprint error_name error_desc severity ltr _ eclass (_, node_key) loc ml_loc_opt ekind in_footprint error_name error_desc severity ltr _ eclass
= =
if in_footprint && error_filter error_desc error_name then let source_file =
match file_opt with
| Some file -> file
| None -> loc.Location.file in
if in_footprint && error_filter source_file error_desc error_name then
let kind = Exceptions.err_kind_string ekind in let kind = Exceptions.err_kind_string ekind in
let bug_type = Localise.to_string error_name in let bug_type = Localise.to_string error_name in
let procedure_id = Procname.to_filename (Specs.get_proc_name summary) in let procedure_id = Procname.to_filename procname in
let file = let file = DB.source_file_to_string source_file in
DB.source_file_to_string summary.Specs.attributes.ProcAttributes.loc.Location.file in
let file = expand_links_under_buck_out file in let file = expand_links_under_buck_out file in
let json_ml_loc = match ml_loc_opt with let json_ml_loc = match ml_loc_opt with
| Some (file, lnum, cnum, enum) when Config.reports_include_ml_loc -> | Some (file, lnum, cnum, enum) when Config.reports_include_ml_loc ->
@ -397,7 +396,7 @@ module BugsJson = struct
qualifier = error_desc_to_plain_string error_desc; qualifier = error_desc_to_plain_string error_desc;
severity = severity; severity = severity;
line = loc.Location.line; line = loc.Location.line;
procedure = Procname.to_string (Specs.get_proc_name summary); procedure = Procname.to_string procname;
procedure_id = procedure_id; procedure_id = procedure_id;
file = file; file = file;
bug_trace = loc_trace_to_jsonbug_record ltr ekind; bug_trace = loc_trace_to_jsonbug_record ltr ekind;
@ -410,20 +409,24 @@ module BugsJson = struct
if not !is_first_item then pp "," else is_first_item := false; if not !is_first_item then pp "," else is_first_item := false;
pp "%s@?" (Jsonbug_j.string_of_jsonbug bug) in pp "%s@?" (Jsonbug_j.string_of_jsonbug bug) in
Errlog.iter pp_row err_log Errlog.iter pp_row err_log
end end
module BugsTxt = struct module IssuesTxt = struct
(** Write bug report in text format *) (** Write bug report in text format *)
let pp_bugs error_filter fmt summary = let pp_issues_of_error_log fmt error_filter _ file_opt _ err_log =
let err_log = summary.Specs.attributes.ProcAttributes.err_log in
let pp_row (node_id, node_key) loc _ ekind in_footprint error_name error_desc _ _ _ _ = let pp_row (node_id, node_key) loc _ ekind in_footprint error_name error_desc _ _ _ _ =
if in_footprint && error_filter error_desc error_name then let source_file =
match file_opt with
| Some file -> file
| None -> loc.Location.file in
if in_footprint && error_filter source_file error_desc error_name then
Exceptions.pp_err (node_id, node_key) loc ekind error_name error_desc None fmt () in Exceptions.pp_err (node_id, node_key) loc ekind error_name error_desc None fmt () in
Errlog.iter pp_row err_log Errlog.iter pp_row err_log
end end
module BugsXml = struct module IssuesXml = struct
let xml_bugs_id = ref 0 let xml_issues_id = ref 0
let include_precondition_tree = false let include_precondition_tree = false
let loc_trace_to_xml linereader ltr = let loc_trace_to_xml linereader ltr =
@ -453,12 +456,15 @@ module BugsXml = struct
(node_tags_to_xml lt.Errlog.lt_node_tags)] in (node_tags_to_xml lt.Errlog.lt_node_tags)] in
IList.rev (IList.rev_map loc_to_xml ltr) IList.rev (IList.rev_map loc_to_xml ltr)
(** print bugs from summary in xml *) (** print issues from summary in xml *)
let pp_bugs error_filter linereader fmt summary = let pp_issues_of_error_log fmt error_filter linereader file_opt proc_name err_log =
let err_log = summary.Specs.attributes.ProcAttributes.err_log in
let do_row let do_row
(_, node_key) loc _ ekind in_footprint error_name error_desc severity ltr pre_opt eclass = (_, node_key) loc _ ekind in_footprint error_name error_desc severity ltr pre_opt eclass =
if in_footprint && error_filter error_desc error_name then let source_file =
match file_opt with
| Some file -> file
| None -> loc.Location.file in
if in_footprint && error_filter source_file error_desc error_name then
let err_desc_string = error_desc_to_xml_string error_desc in let err_desc_string = error_desc_to_xml_string error_desc in
let precondition_tree () = match pre_opt with let precondition_tree () = match pre_opt with
| None -> [] | None -> []
@ -470,15 +476,14 @@ module BugsXml = struct
let kind = Exceptions.err_kind_string ekind in let kind = Exceptions.err_kind_string ekind in
let type_str = Localise.to_string error_name in let type_str = Localise.to_string error_name in
let tree = let tree =
incr xml_bugs_id; incr xml_issues_id;
let attributes = [("id", string_of_int !xml_bugs_id) ] in let attributes = [("id", string_of_int !xml_issues_id) ] in
let error_class = Exceptions.err_class_string eclass in let error_class = Exceptions.err_class_string eclass in
let error_line = string_of_int loc.Location.line in let error_line = string_of_int loc.Location.line in
let proc_name = Specs.get_proc_name summary in
let procedure_name = Procname.to_string proc_name in let procedure_name = Procname.to_string proc_name in
let procedure_id = Procname.to_filename proc_name in let procedure_id = Procname.to_filename proc_name in
let filename = let filename =
DB.source_file_to_string summary.Specs.attributes.ProcAttributes.loc.Location.file in DB.source_file_to_string source_file in
let bug_hash = get_bug_hash kind type_str procedure_id filename node_key error_desc in let bug_hash = get_bug_hash kind type_str procedure_id filename node_key error_desc in
let forest = let forest =
[ [
@ -503,12 +508,12 @@ module BugsXml = struct
Io_infer.Xml.pp_inner_node fmt tree in Io_infer.Xml.pp_inner_node fmt tree in
Errlog.iter do_row err_log Errlog.iter do_row err_log
(** print the opening of the bugs xml file *) (** print the opening of the issues xml file *)
let pp_bugs_open fmt () = let pp_issues_open fmt () =
Io_infer.Xml.pp_open fmt "bugs" Io_infer.Xml.pp_open fmt "bugs"
(** print the closing of the bugs xml file *) (** print the closing of the issues xml file *)
let pp_bugs_close fmt () = let pp_issues_close fmt () =
Io_infer.Xml.pp_close fmt "bugs" Io_infer.Xml.pp_close fmt "bugs"
end end
@ -702,6 +707,57 @@ module Report = struct
let pp_stats fmt stats = let pp_stats fmt stats =
Stats.pp fmt stats Stats.pp fmt stats
end
module Summary = struct
let pp_summary_out summary =
let proc_name = Specs.get_proc_name summary in
if Config.quiet then ()
else L.stdout "Procedure: %a@\n%a@."
Procname.pp proc_name (Specs.pp_summary pe_text Config.whole_seconds) summary
(** Write proc summary to latex file *)
let write_summary_latex fmt summary =
let proc_name = Specs.get_proc_name summary in
Latex.pp_section fmt ("Analysis of function "
^ (Latex.convert_string (Procname.to_string proc_name)));
F.fprintf fmt "@[<v>%a@]" (Specs.pp_summary (pe_latex Black) Config.whole_seconds) summary
let pp_summary_xml summary fname =
if Config.xml_specs then
(let base = DB.chop_extension (DB.filename_from_string fname) in
let xml_file = DB.filename_add_suffix base ".xml" in
let specs = Specs.get_specs_from_payload summary in
if not (DB.file_exists xml_file)
|| DB.file_modified_time (DB.filename_from_string fname) > DB.file_modified_time xml_file
then
let xml_out = create_outfile (DB.filename_to_string xml_file) in
do_outf xml_out (fun outf ->
Dotty.print_specs_xml
(Specs.get_signature summary)
specs summary.Specs.attributes.ProcAttributes.loc outf.fmt;
close_outf outf))
let print_summary_dot_svg summary fname =
if Config.svg then begin
let base = DB.chop_extension (DB.filename_from_string fname) in
let specs = Specs.get_specs_from_payload summary in
let dot_file = DB.filename_add_suffix base ".dot" in
let svg_file = DB.filename_add_suffix base ".svg" in
if not (DB.file_exists dot_file)
|| DB.file_modified_time (DB.filename_from_string fname) > DB.file_modified_time dot_file
then
Dotty.pp_speclist_dotty_file base specs;
if not (DB.file_exists svg_file)
|| DB.file_modified_time dot_file > DB.file_modified_time svg_file
then
ignore (Sys.command ("dot -Tsvg \"" ^
(DB.filename_to_string dot_file) ^
"\" >\"" ^
(DB.filename_to_string svg_file) ^"\""))
end
end end
(** Categorize the preconditions of specs and print stats *) (** Categorize the preconditions of specs and print stats *)
@ -736,66 +792,144 @@ module PreconditionStats = struct
L.stdout "Procedures with data constraints: %d@." !nr_dataconstraints L.stdout "Procedures with data constraints: %d@." !nr_dataconstraints
end end
let error_filter filters proc_name file error_desc error_name =
let always_report () =
Localise.error_desc_extract_tag_value error_desc "always_report" = "true" in
(Config.write_html || not (Localise.equal error_name Localise.skip_function)) &&
(filters.Inferconfig.path_filter file
|| always_report ()) &&
filters.Inferconfig.error_filter error_name &&
filters.Inferconfig.proc_filter proc_name
type report_kind =
| Issues
| Procs
| Stats
| Calls
| Summary
type bug_format =
| Json of outfile
| Csv of outfile
| Text of outfile
| Xml of outfile
| Latex of outfile
let pp_issues_in_format format =
match format with
| Json outf -> IssuesJson.pp_issues_of_error_log outf.fmt
| Csv outf -> IssuesCsv.pp_issues_of_error_log outf.fmt
| Text outf -> IssuesTxt.pp_issues_of_error_log outf.fmt
| Xml outf -> IssuesXml.pp_issues_of_error_log outf.fmt
| Latex _ -> failwith "Printing issues in latex is not implemented"
let pp_procs_in_format format =
match format with
| Csv outf -> ProcsCsv.pp_summary outf.fmt
| Xml outf -> ProcsXml.pp_proc outf.fmt
| Json _ | Latex _ | Text _ ->
failwith "Printing procs in json/latex/text is not implemented"
let pp_calls_in_format format =
match format with
| Csv outf -> CallsCsv.pp_calls outf.fmt
| Json _ | Text _ | Xml _ | Latex _ ->
failwith "Printing calls in json/text/xml/latex is not implemented"
let pp_stats_in_format format =
match format with
| Csv _ -> Stats.process_summary
| Json _ | Text _ | Xml _ | Latex _ ->
failwith "Printing stats in json/text/xml/latex is not implemented"
let pp_summary_in_format format =
match format with
| Latex outfile -> Summary.write_summary_latex outfile.fmt
| Json _ | Csv _ | Text _ | Xml _ ->
failwith "Printing summary in json/csv/text/xml is not implemented"
let pp_issues_of_error_log error_filter linereader file_opt procname err_log bug_format_list =
let pp_issues_in_format format =
let pp_issues = pp_issues_in_format format in
pp_issues error_filter linereader file_opt procname err_log in
IList.iter pp_issues_in_format bug_format_list
let pp_issues error_filter linereader summary bug_format_list =
let err_log = summary.Specs.attributes.ProcAttributes.err_log in
let procname = Specs.get_proc_name summary in
let file = summary.Specs.attributes.ProcAttributes.loc.Location.file in
pp_issues_of_error_log error_filter linereader (Some file) procname err_log bug_format_list
let pp_procs top_proc_set summary procs_format_list =
let pp_procs_in_format format =
let pp_procs = pp_procs_in_format format in
pp_procs top_proc_set summary in
IList.iter pp_procs_in_format procs_format_list
let pp_calls summary calls_format_list =
let pp_calls_in_format format =
let pp_calls = pp_calls_in_format format in
pp_calls summary in
IList.iter pp_calls_in_format calls_format_list
let pp_stats error_filter linereader summary stats stats_format_list =
let pp_stats_in_format format =
let pp_stats = pp_stats_in_format format in
pp_stats error_filter summary linereader stats in
IList.iter pp_stats_in_format stats_format_list
let pp_summary summary fname summary_format_list =
let pp_summary_in_format format =
let pp_summary = pp_summary_in_format format in
pp_summary summary in
IList.iter pp_summary_in_format summary_format_list;
Summary.pp_summary_out summary;
Summary.pp_summary_xml summary fname;
Summary.print_summary_dot_svg summary fname
let pp_summary_by_report_kind formats_by_report_kind summary top_proc_set fname error_filter
linereader stats file =
let pp_summary_by_report_kind (report_kind, format_list) =
match report_kind, format_list with
| Issues, _::_ ->
pp_issues error_filter linereader summary format_list
| Procs, _::_ ->
pp_procs top_proc_set summary format_list
| Stats, _::_ ->
pp_stats (error_filter file) linereader summary stats format_list
| Calls, _::_ ->
pp_calls summary format_list
| Summary, _ ->
pp_summary summary fname format_list
| _ -> () in
IList.iter pp_summary_by_report_kind formats_by_report_kind
let pp_lint_issues_by_report_kind formats_by_report_kind error_filter linereader
procname error_log =
let pp_summary_by_report_kind (report_kind, format_list) =
match report_kind, format_list with
| Issues, _::_ ->
pp_issues_of_error_log error_filter linereader None procname error_log format_list
| _ -> () in
IList.iter pp_summary_by_report_kind formats_by_report_kind
(** Process lint issues of a procedure *)
let pp_lint_issues filters formats_by_report_kind linereader procname error_log =
let error_filter = error_filter filters procname in
pp_lint_issues_by_report_kind formats_by_report_kind error_filter linereader procname
error_log
(** Process a summary *) (** Process a summary *)
let process_summary filters linereader stats (top_proc_set: Procname.Set.t) (fname, summary) = let process_summary filters formats_by_report_kind linereader stats top_proc_set (fname, summary) =
let file = summary.Specs.attributes.ProcAttributes.loc.Location.file in
let proc_name = Specs.get_proc_name summary in let proc_name = Specs.get_proc_name summary in
let base = DB.chop_extension (DB.filename_from_string fname) in let error_filter = error_filter filters proc_name in
let pp_simple_saved = !Config.pp_simple in let pp_simple_saved = !Config.pp_simple in
Config.pp_simple := true; Config.pp_simple := true;
if Config.quiet then () pp_summary_by_report_kind formats_by_report_kind summary top_proc_set fname error_filter
else L.stdout "Procedure: %a@\n%a@." linereader stats file;
Procname.pp proc_name (Specs.pp_summary pe_text Config.whole_seconds) summary;
let error_filter error_desc error_name =
let always_report () =
Localise.error_desc_extract_tag_value error_desc "always_report" = "true" in
(Config.write_html || not (Localise.equal error_name Localise.skip_function)) &&
(filters.Inferconfig.path_filter summary.Specs.attributes.ProcAttributes.loc.Location.file
|| always_report ()) &&
filters.Inferconfig.error_filter error_name &&
filters.Inferconfig.proc_filter proc_name in
do_outf Config.procs_csv (fun outf -> ProcsCsv.pp_summary top_proc_set outf.fmt summary);
do_outf Config.calls_csv (fun outf -> CallsCsv.pp_calls outf.fmt summary);
do_outf Config.procs_xml (fun outf -> ProcsXml.pp_proc top_proc_set outf.fmt summary);
do_outf Config.bugs_csv (fun outf -> BugsCsv.pp_bugs error_filter outf.fmt summary);
do_outf Config.bugs_json (fun outf -> BugsJson.pp_bugs error_filter outf.fmt summary);
do_outf Config.bugs_txt (fun outf -> BugsTxt.pp_bugs error_filter outf.fmt summary);
do_outf Config.bugs_xml (fun outf -> BugsXml.pp_bugs error_filter linereader outf.fmt summary);
do_outf Config.report (fun _ -> Stats.process_summary error_filter summary linereader stats);
if Config.precondition_stats then PreconditionStats.do_summary proc_name summary; if Config.precondition_stats then PreconditionStats.do_summary proc_name summary;
Config.pp_simple := pp_simple_saved; Config.pp_simple := pp_simple_saved
do_outf Config.latex (fun outf -> write_summary_latex outf.fmt summary);
if Config.svg then begin
let specs = Specs.get_specs_from_payload summary in
let dot_file = DB.filename_add_suffix base ".dot" in
let svg_file = DB.filename_add_suffix base ".svg" in
if not (DB.file_exists dot_file)
|| DB.file_modified_time (DB.filename_from_string fname) > DB.file_modified_time dot_file
then
Dotty.pp_speclist_dotty_file base specs;
if not (DB.file_exists svg_file)
|| DB.file_modified_time dot_file > DB.file_modified_time svg_file
then
ignore (Sys.command ("dot -Tsvg \"" ^
(DB.filename_to_string dot_file) ^
"\" >\"" ^
(DB.filename_to_string svg_file) ^"\""))
end;
if Config.xml_specs then begin
let xml_file = DB.filename_add_suffix base ".xml" in
let specs = Specs.get_specs_from_payload summary in
if not (DB.file_exists xml_file)
|| DB.file_modified_time (DB.filename_from_string fname) > DB.file_modified_time xml_file
then
begin
let xml_out = create_outfile (DB.filename_to_string xml_file) in
do_outf xml_out (fun outf ->
Dotty.print_specs_xml
(Specs.get_signature summary)
specs summary.Specs.attributes.ProcAttributes.loc outf.fmt;
close_outf outf)
end
end
(* ignore (Sys.command ("open " ^ base ^ ".svg")) *)
module AnalysisResults = struct module AnalysisResults = struct
type t = (string * Specs.summary) list type t = (string * Specs.summary) list
@ -919,46 +1053,98 @@ let register_perf_stats_report () =
DB.create_dir stats_dir ; DB.create_dir stats_dir ;
PerfStats.register_report_at_exit stats_file PerfStats.register_report_at_exit stats_file
let () = let init_issues_format_list () =
register_perf_stats_report () ; let csv_format = Option.map_default (fun out_file -> [Csv out_file]) [] Config.bugs_csv in
let json_format = Option.map_default (fun out_file -> [Json out_file]) [] Config.bugs_json in
handle_source_file_copy_option (); let txt_format = Option.map_default (fun out_file -> [Text out_file]) [] Config.bugs_txt in
let iterate_summaries = AnalysisResults.get_summary_iterator () in let xml_format = Option.map_default (fun out_file -> [Xml out_file]) [] Config.bugs_xml in
csv_format @ json_format @ txt_format @ xml_format
let init_procs_format_list () =
let csv_format = Option.map_default (fun out_file -> [Csv out_file]) [] Config.procs_csv in
let xml_format = Option.map_default (fun out_file -> [Xml out_file]) [] Config.procs_xml in
csv_format @ xml_format
let init_calls_format_list () =
let csv_format = Option.map_default (fun out_file -> [Csv out_file]) [] Config.calls_csv in
csv_format
let init_stats_format_list () =
let csv_format = Option.map_default (fun out_file -> [Csv out_file]) [] Config.report in
csv_format
let init_summary_format_list () =
let latex_format = Option.map_default (fun out_file -> [Latex out_file]) [] Config.latex in
latex_format
let init_files format_list_by_kind =
let init_files_of_report_kind (report_kind, format_list) =
let init_files_of_format format =
match format, report_kind with
| Csv outfile, Stats -> Report.pp_header outfile.fmt ();
| Csv outfile, Issues -> IssuesCsv.pp_header outfile.fmt ()
| Json outfile, Issues -> IssuesJson.pp_json_open outfile.fmt ()
| Xml outfile, Issues -> IssuesXml.pp_issues_open outfile.fmt ()
| Xml outfile, Procs -> ProcsXml.pp_procs_open outfile.fmt ()
| Latex outfile, Summary -> begin_latex_file outfile.fmt
| _ -> () in
IList.iter init_files_of_format format_list in
IList.iter init_files_of_report_kind format_list_by_kind
let finalize_and_close_files format_list_by_kind stats pdflatex =
let close_files_of_report_kind (report_kind, format_list) =
let close_files_of_format format =
match format, report_kind with
| Csv outfile, Stats ->
F.fprintf outfile.fmt "%a@?" Report.pp_stats stats;close_outf outfile
| Csv outfile, _ | Text outfile, _ -> close_outf outfile
| Json outfile, Issues ->
IssuesJson.pp_json_close outfile.fmt ();close_outf outfile
| Xml outfile, Issues ->
IssuesXml.pp_issues_close outfile.fmt ();close_outf outfile
| Xml outfile, Procs ->
ProcsXml.pp_procs_close outfile.fmt ();close_outf outfile
| Latex outfile, Summary ->
Latex.pp_end outfile.fmt ();close_outf outfile;
pdflatex outfile.fname;
let pdf_name = (Filename.chop_extension outfile.fname) ^ ".pdf" in
ignore (Sys.command ("open " ^ pdf_name))
| _ -> () in
IList.iter close_files_of_format format_list in
IList.iter close_files_of_report_kind format_list_by_kind
let pp_summary_and_issues formats_by_report_kind =
init_files formats_by_report_kind;
let pdflatex fname = ignore (Sys.command ("pdflatex " ^ fname)) in
let stats = Stats.create () in
let linereader = Printer.LineReader.create () in
let filters = let filters =
match Config.analyzer with match Config.analyzer with
| None -> Inferconfig.do_not_filter | None -> Inferconfig.do_not_filter
| Some analyzer -> Inferconfig.create_filters analyzer in | Some analyzer -> Inferconfig.create_filters analyzer in
let iterate_summaries = AnalysisResults.get_summary_iterator () in
let pdflatex fname = ignore (Sys.command ("pdflatex " ^ fname)) in
do_outf Config.latex (fun outf -> begin_latex_file outf.fmt);
do_outf Config.procs_csv (fun outf -> ProcsCsv.pp_header outf.fmt ());
do_outf Config.procs_xml (fun outf -> ProcsXml.pp_procs_open outf.fmt ());
do_outf Config.calls_csv (fun outf -> CallsCsv.pp_header outf.fmt ());
do_outf Config.bugs_csv (fun outf -> BugsCsv.pp_header outf.fmt ());
do_outf Config.bugs_json (fun outf -> BugsJson.pp_json_open outf.fmt ());
do_outf Config.bugs_xml (fun outf -> BugsXml.pp_bugs_open outf.fmt ());
do_outf Config.report (fun outf -> Report.pp_header outf.fmt ());
let top_proc = TopProcedures.create () in let top_proc = TopProcedures.create () in
let top_proc_set = TopProcedures.top_set top_proc in
if !compute_top_procedures && (Config.procs_csv != None || Config.procs_xml != None) if !compute_top_procedures && (Config.procs_csv != None || Config.procs_xml != None)
then iterate_summaries (TopProcedures.process_summary top_proc); then iterate_summaries (TopProcedures.process_summary top_proc);
let top_proc_set = TopProcedures.top_set top_proc in iterate_summaries (
let linereader = Printer.LineReader.create () in process_summary filters formats_by_report_kind linereader stats top_proc_set);
let stats = Stats.create () in if Config.precondition_stats then PreconditionStats.pp_stats ();
iterate_summaries (process_summary filters linereader stats top_proc_set); (LintIssues.load_issues_to_errlog_map Config.lint_issues_dir_name;
do_outf Config.procs_csv close_outf; Procname.Map.iter (pp_lint_issues filters formats_by_report_kind linereader)
do_outf Config.procs_xml (fun outf -> ProcsXml.pp_procs_close outf.fmt (); close_outf outf); !LintIssues.errLogMap);
do_outf Config.bugs_csv close_outf; finalize_and_close_files formats_by_report_kind stats pdflatex
do_outf Config.bugs_json (fun outf -> BugsJson.pp_json_close outf.fmt (); close_outf outf);
do_outf Config.bugs_json close_outf; let () =
do_outf Config.calls_csv close_outf; let formats_by_report_kind =
do_outf Config.bugs_txt close_outf; [
do_outf Config.bugs_xml (fun outf -> BugsXml.pp_bugs_close outf.fmt (); close_outf outf); ( Issues, (init_issues_format_list ()) );
do_outf Config.latex (fun outf -> ( Procs, (init_procs_format_list ()) );
Latex.pp_end outf.fmt (); ( Calls, (init_calls_format_list ()) );
close_outf outf; ( Stats, (init_stats_format_list ()) );
pdflatex outf.fname; ( Summary, (init_summary_format_list ()) )
let pdf_name = (Filename.chop_extension outf.fname) ^ ".pdf" in ] in
ignore (Sys.command ("open " ^ pdf_name))); register_perf_stats_report () ;
do_outf Config.report handle_source_file_copy_option ();
(fun outf -> F.fprintf outf.fmt "%a@?" Report.pp_stats stats; close_outf outf); (* print issues *)
if Config.precondition_stats then PreconditionStats.pp_stats () pp_summary_and_issues formats_by_report_kind

@ -0,0 +1,53 @@
(*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
(** Module to store a set of issues per procedure *)
open! Utils
let errLogMap = ref Procname.Map.empty
let get_err_log procname =
try Procname.Map.find procname !errLogMap
with Not_found ->
let errlog = Errlog.empty () in
errLogMap := Procname.Map.add procname errlog !errLogMap; errlog
let lint_issues_serializer : (Errlog.t Procname.Map.t) Serialization.serializer =
Serialization.create_serializer Serialization.lint_issues_key
(** Save issues to a file *)
let store_issues filename errLogMap =
Serialization.to_file lint_issues_serializer filename errLogMap
(** Load issues from the given file *)
let load_issues issues_file =
Serialization.from_file lint_issues_serializer issues_file
(** Load all the lint issues in the given dir and update the issues map *)
let load_issues_to_errlog_map dir =
let issues_dir = Filename.concat Config.results_dir dir in
let children_opt = try Some (Sys.readdir issues_dir) with Sys_error _ -> None in
let load_issues_to_map issues_file =
let file = DB.filename_from_string (Filename.concat issues_dir issues_file) in
match load_issues file with
| Some map ->
errLogMap := Procname.Map.merge (
fun _ issues1 issues2 ->
match issues1, issues2 with
| Some issues1, Some issues2 ->
Errlog.update issues1 issues2; Some issues1
| Some issues1, None -> Some issues1
| None, Some issues2 -> Some issues2
| None, None -> None
) !errLogMap map
| None -> () in
match children_opt with
| Some children -> Array.iter load_issues_to_map children
| None -> ()

@ -0,0 +1,21 @@
(*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
(** Module to store a set of issues per procedure *)
val errLogMap : Errlog.t Procname.Map.t ref
(** Save issues to a file *)
val get_err_log : Procname.t -> Errlog.t
(** Load issues from the given file *)
val store_issues : DB.filename -> Errlog.t Procname.Map.t -> unit
(** Load all the lint issues in the given dir and update the issues map *)
val load_issues_to_errlog_map : string -> unit

@ -21,9 +21,9 @@ type key = int
(** current key for tenv, procedure summary, cfg, error trace, call graph *) (** current key for tenv, procedure summary, cfg, error trace, call graph *)
let tenv_key, summary_key, cfg_key, trace_key, cg_key, let tenv_key, summary_key, cfg_key, trace_key, cg_key,
analysis_results_key, cluster_key, attributes_key = analysis_results_key, cluster_key, attributes_key, lint_issues_key =
425184201, 160179325, 1062389858, 221487792, 477305409, 425184201, 160179325, 1062389858, 221487792, 477305409,
799050016, 579094948, 972393003 799050016, 579094948, 972393003, 852343110
(** version of the binary files, to be incremented for each change *) (** version of the binary files, to be incremented for each change *)
let version = 26 let version = 26

@ -54,3 +54,6 @@ val to_file : 'a serializer -> DB.filename -> 'a -> unit
(** current key for an error trace *) (** current key for an error trace *)
val trace_key : key val trace_key : key
(** current key for lint issues *)
val lint_issues_key : key

@ -27,8 +27,9 @@ let compute_icfg tenv ast =
Printing.log_out "\n Start creating icfg\n"; Printing.log_out "\n Start creating icfg\n";
let cg = Cg.create () in let cg = Cg.create () in
let cfg = Cfg.Node.create_cfg () in let cfg = Cfg.Node.create_cfg () in
IList.iter (CFrontend_declImpl.translate_one_declaration tenv cg cfg `DeclTraversal) if not Config.linters_mode_enabled then
decl_list; IList.iter (CFrontend_declImpl.translate_one_declaration tenv cg cfg `DeclTraversal)
decl_list;
Printing.log_out "\n Finished creating icfg\n"; Printing.log_out "\n Finished creating icfg\n";
(cg, cfg) (cg, cfg)
| _ -> assert false (* NOTE: Assumes that an AST alsways starts with a TranslationUnitDecl *) | _ -> assert false (* NOTE: Assumes that an AST alsways starts with a TranslationUnitDecl *)
@ -60,7 +61,7 @@ let do_source_file source_file ast =
let call_graph, cfg = compute_icfg tenv ast in let call_graph, cfg = compute_icfg tenv ast in
Printing.log_out "\n End building call/cfg graph for '%s'.\n" Printing.log_out "\n End building call/cfg graph for '%s'.\n"
(DB.source_file_to_string source_file); (DB.source_file_to_string source_file);
CFrontend_checkers_main.do_frontend_checks cfg call_graph ast; CFrontend_checkers_main.do_frontend_checks cfg call_graph source_file ast;
(* This part below is a boilerplate in every frontends. *) (* This part below is a boilerplate in every frontends. *)
(* This could be moved in the cfg_infer module *) (* This could be moved in the cfg_infer module *)
let source_dir = DB.source_dir_from_source_file !DB.current_source in let source_dir = DB.source_dir_from_source_file !DB.current_source in

@ -48,9 +48,18 @@ let context_with_ck_set context decl_list =
else else
context context
let do_frontend_checks cfg cg ast = let store_issues source_file =
let abbrev_source_file = DB.source_file_encoding source_file in
let lint_issues_dir = Filename.concat Config.results_dir Config.lint_issues_dir_name in
DB.create_dir lint_issues_dir;
let lint_issues_file =
DB.filename_from_string (Filename.concat lint_issues_dir (abbrev_source_file ^ ".issue")) in
LintIssues.store_issues lint_issues_file !LintIssues.errLogMap
let do_frontend_checks cfg cg source_file ast =
match ast with match ast with
| Clang_ast_t.TranslationUnitDecl(_, decl_list, _, _) -> | Clang_ast_t.TranslationUnitDecl(_, decl_list, _, _) ->
let context = context_with_ck_set CLintersContext.empty decl_list in let context = context_with_ck_set CLintersContext.empty decl_list in
IList.iter (do_frontend_checks_decl context cfg cg) decl_list IList.iter (do_frontend_checks_decl context cfg cg) decl_list;
if Config.linters_mode_enabled then store_issues source_file
| _ -> assert false (* NOTE: Assumes that an AST alsways starts with a TranslationUnitDecl *) | _ -> assert false (* NOTE: Assumes that an AST alsways starts with a TranslationUnitDecl *)

@ -7,4 +7,4 @@
* 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.
*) *)
val do_frontend_checks : Cfg.cfg -> Cg.t -> Clang_ast_t.decl -> unit val do_frontend_checks : Cfg.cfg -> Cg.t -> DB.source_file -> Clang_ast_t.decl -> unit

@ -76,19 +76,12 @@ let while_stmt_checker_list = [CFrontend_checkers.bad_pointer_comparison_warning
let checker_for_while_stmt stmt_info cond checker context = let checker_for_while_stmt stmt_info cond checker context =
checker context stmt_info cond checker context stmt_info cond
let errLogMap = ref Procname.Map.empty
let get_err_log cfg cg method_decl_opt loc = let get_err_log cfg cg method_decl_opt loc =
let procname = match method_decl_opt with
| Some method_decl -> General_utils.procname_of_decl method_decl
| None -> General_utils.get_procname_for_frontend_checks loc in
if Config.linters_mode_enabled then if Config.linters_mode_enabled then
let procname = match method_decl_opt with LintIssues.get_err_log procname
| Some method_decl -> General_utils.procname_of_decl method_decl
| None -> General_utils.get_procname_for_frontend_checks loc in
try Procname.Map.find procname !errLogMap
with Not_found ->
let errlog = Errlog.empty () in
errLogMap := Procname.Map.add procname errlog !errLogMap; errlog
else else
let pdesc = CMethod_trans.get_method_for_frontend_checks cfg cg loc in let pdesc = CMethod_trans.get_method_for_frontend_checks cfg cg loc in
Cfg.Procdesc.get_err_log pdesc Cfg.Procdesc.get_err_log pdesc

@ -18,7 +18,5 @@ val run_frontend_checkers_on_stmt :
Cfg.cfg -> Cg.t -> Clang_ast_t.decl -> Clang_ast_t.stmt -> CLintersContext.context Cfg.cfg -> Cg.t -> Clang_ast_t.decl -> Clang_ast_t.stmt -> CLintersContext.context
(* Run frontend checkers on a declaration *) (* Run frontend checkers on a declaration *)
val run_frontend_checkers_on_decl : val run_frontend_checkers_on_decl : CLintersContext.context -> Cfg.cfg -> Cg.t ->
CLintersContext.context -> Cfg.cfg -> Cg.t -> Clang_ast_t.decl -> CLintersContext.context Clang_ast_t.decl -> CLintersContext.context
val errLogMap : Errlog.t Procname.Map.t ref

Loading…
Cancel
Save