You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
7.0 KiB
182 lines
7.0 KiB
(*
|
|
* 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.
|
|
*)
|
|
open! IStd
|
|
open! PVariant
|
|
module L = Logging
|
|
|
|
let aggregated_stats_filename = "aggregated_stats.json"
|
|
|
|
let aggregated_stats_by_target_filename = "aggregated_stats_by_target.json"
|
|
|
|
let json_files_to_ignore_regex =
|
|
Str.regexp
|
|
( ".*\\(" ^ Str.quote aggregated_stats_filename ^ "\\|"
|
|
^ Str.quote aggregated_stats_by_target_filename ^ "\\)$" )
|
|
|
|
|
|
let dir_exists dir = Sys.is_directory dir = `Yes
|
|
|
|
let find_json_files_in_dir dir =
|
|
let is_valid_json_file path =
|
|
let s = Unix.lstat path in
|
|
let json_regex = Str.regexp_case_fold ".*\\.json$" in
|
|
not (Str.string_match json_files_to_ignore_regex path 0) && Str.string_match json_regex path 0
|
|
&& Polymorphic_compare.( = ) s.st_kind Unix.S_REG
|
|
in
|
|
match dir_exists dir with
|
|
| true ->
|
|
let content = Array.to_list (Sys.readdir dir) in
|
|
let content_with_path = List.map ~f:(fun p -> Filename.concat dir p) content in
|
|
List.filter ~f:is_valid_json_file content_with_path
|
|
| false ->
|
|
[]
|
|
|
|
|
|
type stats_paths =
|
|
{frontend_paths: string list; backend_paths: string list; reporting_paths: string list}
|
|
|
|
type origin = Buck_out of (string * stats_paths) list | Infer_out of stats_paths
|
|
|
|
let find_stats_files_in_dir dir =
|
|
let frontend_paths =
|
|
find_json_files_in_dir (Filename.concat dir Config.frontend_stats_dir_name)
|
|
in
|
|
let backend_paths = find_json_files_in_dir (Filename.concat dir Config.backend_stats_dir_name) in
|
|
let reporting_paths =
|
|
find_json_files_in_dir (Filename.concat dir Config.reporting_stats_dir_name)
|
|
in
|
|
{frontend_paths; backend_paths; reporting_paths}
|
|
|
|
|
|
let load_data_from_infer_deps file =
|
|
let error msg = Printf.sprintf ("Error reading '%s': " ^^ msg) file in
|
|
let extract_target_and_path line =
|
|
match String.split ~on:'\t' line with
|
|
| target :: _ :: path :: _ ->
|
|
if dir_exists path then Ok (target, path)
|
|
else Error (error "path '%s' is not a valid directory" path)
|
|
| _ ->
|
|
Error (error "malformed input")
|
|
in
|
|
let parse_lines lines = List.map lines ~f:extract_target_and_path |> Result.all in
|
|
Utils.read_file file |> Result.map_error ~f:(fun msg -> error "%s" msg)
|
|
|> Result.bind ~f:parse_lines
|
|
|
|
|
|
let collect_all_stats_files () =
|
|
let infer_out = Config.results_dir in
|
|
let concatenate_paths p1 p2 = if Filename.is_relative p2 then Filename.concat p1 p2 else p2 in
|
|
match Config.buck_out with
|
|
| Some p ->
|
|
if dir_exists p then
|
|
let data =
|
|
load_data_from_infer_deps (Filename.concat infer_out Config.buck_infer_deps_file_name)
|
|
in
|
|
match data with
|
|
| Ok r ->
|
|
let buck_out_parent = Filename.concat p Filename.parent_dir_name in
|
|
let targets_files =
|
|
List.map
|
|
~f:(fun (t, p) ->
|
|
(t, find_stats_files_in_dir (concatenate_paths buck_out_parent p)) )
|
|
r
|
|
in
|
|
Ok (Buck_out targets_files)
|
|
| Error _ as e ->
|
|
e
|
|
else Error ("buck-out path '" ^ p ^ "' not found")
|
|
| None ->
|
|
Ok (Infer_out (find_stats_files_in_dir infer_out))
|
|
|
|
|
|
let aggregate_stats_files paths =
|
|
let open_json_file file = Yojson.Basic.from_file file in
|
|
let load_stats paths =
|
|
List.map ~f:(fun path -> PerfStats.from_json (open_json_file path)) paths
|
|
in
|
|
let all_perf_stats = load_stats paths in
|
|
match all_perf_stats with [] -> None | _ -> Some (PerfStats.aggregate all_perf_stats)
|
|
|
|
|
|
type json_aggregated_stats =
|
|
{ frontend_json_data: Yojson.Basic.json option
|
|
; backend_json_data: Yojson.Basic.json option
|
|
; reporting_json_data: Yojson.Basic.json option }
|
|
|
|
let aggregate_all_stats origin =
|
|
let accumulate_paths acc paths =
|
|
{ frontend_paths= paths.frontend_paths @ acc.frontend_paths
|
|
; backend_paths= paths.backend_paths @ acc.backend_paths
|
|
; reporting_paths= paths.reporting_paths @ acc.reporting_paths }
|
|
in
|
|
let empty_stats_paths = {frontend_paths= []; backend_paths= []; reporting_paths= []} in
|
|
let stats_paths =
|
|
match origin with
|
|
| Buck_out tf ->
|
|
List.fold ~f:(fun acc (_, paths) -> accumulate_paths acc paths) ~init:empty_stats_paths tf
|
|
| Infer_out paths ->
|
|
paths
|
|
in
|
|
{ frontend_json_data= aggregate_stats_files stats_paths.frontend_paths
|
|
; backend_json_data= aggregate_stats_files stats_paths.backend_paths
|
|
; reporting_json_data= aggregate_stats_files stats_paths.reporting_paths }
|
|
|
|
|
|
let aggregate_stats_by_target tp =
|
|
let to_json f aggr_stats =
|
|
let collect_valid_stats acc t p = match p with Some v -> (t, v) :: acc | None -> acc in
|
|
let l = List.fold ~f:(fun acc (t, p) -> collect_valid_stats acc t (f p)) ~init:[] aggr_stats in
|
|
match l with [] -> None | _ as v -> Some (`Assoc v)
|
|
in
|
|
let frontend_json_data = to_json (fun p -> aggregate_stats_files p.frontend_paths) tp in
|
|
let backend_json_data = to_json (fun p -> aggregate_stats_files p.backend_paths) tp in
|
|
let reporting_json_data = to_json (fun p -> aggregate_stats_files p.reporting_paths) tp in
|
|
{frontend_json_data; backend_json_data; reporting_json_data}
|
|
|
|
|
|
let generate_files () =
|
|
let infer_out = Config.results_dir in
|
|
let stats_files = collect_all_stats_files () in
|
|
let origin =
|
|
match stats_files with Ok origin -> origin | Error e -> L.(die InternalError) "%s" e
|
|
in
|
|
let aggregated_frontend_stats_dir = Filename.concat infer_out Config.frontend_stats_dir_name in
|
|
let aggregated_backend_stats_dir = Filename.concat infer_out Config.backend_stats_dir_name in
|
|
let aggregated_reporting_stats_dir = Filename.concat infer_out Config.reporting_stats_dir_name in
|
|
Utils.create_dir aggregated_frontend_stats_dir ;
|
|
Utils.create_dir aggregated_backend_stats_dir ;
|
|
Utils.create_dir aggregated_reporting_stats_dir ;
|
|
let write_to_json_file_opt destfile json =
|
|
match json with Some j -> Utils.write_json_to_file destfile j | None -> ()
|
|
in
|
|
( match origin with
|
|
| Buck_out tp ->
|
|
let j = aggregate_stats_by_target tp in
|
|
write_to_json_file_opt
|
|
(Filename.concat aggregated_frontend_stats_dir aggregated_stats_by_target_filename)
|
|
j.frontend_json_data ;
|
|
write_to_json_file_opt
|
|
(Filename.concat aggregated_backend_stats_dir aggregated_stats_by_target_filename)
|
|
j.backend_json_data ;
|
|
write_to_json_file_opt
|
|
(Filename.concat aggregated_reporting_stats_dir aggregated_stats_by_target_filename)
|
|
j.reporting_json_data
|
|
| Infer_out _ ->
|
|
() ) ;
|
|
let j = aggregate_all_stats origin in
|
|
write_to_json_file_opt
|
|
(Filename.concat aggregated_frontend_stats_dir aggregated_stats_filename)
|
|
j.frontend_json_data ;
|
|
write_to_json_file_opt
|
|
(Filename.concat aggregated_backend_stats_dir aggregated_stats_filename)
|
|
j.backend_json_data ;
|
|
write_to_json_file_opt
|
|
(Filename.concat aggregated_reporting_stats_dir aggregated_stats_filename)
|
|
j.reporting_json_data
|