Skip to content

Commit 1353959

Browse files
yakobowskifacebook-github-bot
authored andcommitted
Detect whether DBWriter can actually be used (facebook#1556)
Summary: ... on systems where it is assumed available. We have had customers under Linux with DBWriter related errors. Some filesystems (e.g. ClearCase, or Vagrant synced folders) do not allow creating a Unix domain socket on them. This patch tries to detect whether sockets are supported, and falls back to --no-dbwriter behavior when they are not. Pull Request resolved: facebook#1556 Reviewed By: ngorogiannis Differential Revision: D32838237 Pulled By: jvillard fbshipit-source-id: 3315e4b1e8
1 parent 44a5c1d commit 1353959

File tree

3 files changed

+49
-16
lines changed

3 files changed

+49
-16
lines changed

infer/src/base/DBWriter.ml

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -364,22 +364,40 @@ module Server = struct
364364

365365
let socket_exists () = in_results_dir ~f:(fun () -> Sys.file_exists_exn socket_name)
366366

367-
let server () =
368-
L.debug Analysis Quiet "Sqlite write daemon: starting up@." ;
367+
(* Error recuperation is done by attempting this function at module initialization time, and
368+
not using DbWriter at all in case it fails. See {!can_use_socket} below. *)
369+
let setup_socket () =
369370
if socket_exists () then L.die InternalError "Sqlite write daemon: socket already exists@." ;
370371
let socket = Unix.socket ~domain:socket_domain ~kind:Unix.SOCK_STREAM ~protocol:0 () in
371372
in_results_dir ~f:(fun () -> Unix.bind socket ~addr:socket_addr) ;
372373
(* [backlog] is (supposedly) the length of the queue for pending connections ;
373374
there are no rules about the implied behaviour though. Here use optimistically
374375
the number of workers, though even that is a guess. *)
375376
Unix.listen socket ~backlog:Config.jobs ;
377+
socket
378+
379+
380+
let remove_socket socket =
381+
in_results_dir ~f:(fun () ->
382+
Unix.close socket ;
383+
Unix.unlink socket_name )
384+
385+
386+
(* Check whether we can create a socket to communicate with the asynchronous DBWriter process. *)
387+
let can_use_socket () =
388+
try
389+
let socket = setup_socket () in
390+
remove_socket socket ;
391+
true
392+
with _ -> false
393+
394+
395+
let server () =
396+
L.debug Analysis Quiet "Sqlite write daemon: starting up@." ;
397+
let socket = setup_socket () in
376398
L.debug Analysis Quiet "Sqlite write daemon: set up complete, waiting for connections@." ;
377-
let shutdown () =
378-
in_results_dir ~f:(fun () ->
379-
Unix.close socket ;
380-
Unix.remove socket_name )
381-
in
382-
Exception.try_finally ~f:(fun () -> server_loop socket) ~finally:shutdown
399+
let finally () = remove_socket socket in
400+
Exception.try_finally ~f:(fun () -> server_loop socket) ~finally
383401

384402

385403
let send cmd =
@@ -420,15 +438,30 @@ module Server = struct
420438
end
421439

422440
let use_daemon =
423-
let is_windows = match Version.build_platform with Windows -> true | Linux | Darwin -> false in
424-
Config.((not is_windows) && dbwriter && (not (buck || genrule_mode)) && jobs > 1)
425-
426-
427-
let perform cmd = if use_daemon then Server.send cmd else Command.execute cmd
441+
lazy
442+
(let is_windows =
443+
match Version.build_platform with Windows -> true | Linux | Darwin -> false
444+
in
445+
Config.((not is_windows) && dbwriter && (not (buck || genrule_mode)) && jobs > 1)
446+
&&
447+
(* Only the main process should try detecting whether the socket can be created.
448+
Otherwise, re-spawned Infer will try to create a socket on top of the existing one. *)
449+
if Config.is_originator then (
450+
let socket_ok = Server.can_use_socket () in
451+
if not socket_ok then
452+
L.user_warning
453+
"Cannot setup the socket to communicate with the database daemon. Performance will be \
454+
impacted. Do you have enough rights to create a Unix socket in directory '%s'?@."
455+
Config.toplevel_results_dir ;
456+
socket_ok )
457+
else Server.socket_exists () )
458+
459+
460+
let perform cmd = if Lazy.force use_daemon then Server.send cmd else Command.execute cmd
428461

429462
let start () = Server.start ()
430463

431-
let stop () = Server.send Command.Terminate
464+
let stop () = try Server.send Command.Terminate with Unix.Unix_error _ -> ()
432465

433466
let replace_attributes ~proc_uid ~proc_name ~attr_kind ~source_file ~proc_attributes ~cfg ~callees =
434467
perform

infer/src/base/DBWriter.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
open! IStd
1010

11-
val use_daemon : bool
11+
val use_daemon : bool Lazy.t
1212
(** indicates that there should be a daemon running *)
1313

1414
val replace_attributes :

infer/src/infer.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ let setup () =
2828
let db_start =
2929
let already_started = ref false in
3030
fun () ->
31-
if (not !already_started) && Config.is_originator && DBWriter.use_daemon then (
31+
if (not !already_started) && Config.is_originator && Lazy.force DBWriter.use_daemon then (
3232
DBWriter.start () ;
3333
Epilogues.register ~f:DBWriter.stop ~description:"Stop Sqlite write daemon" ;
3434
already_started := true )

0 commit comments

Comments
 (0)