Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add use-trace-processor-shell flag to automate large trace processing #248

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/env_vars.ml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
open! Core
open Async

(* Points to a filesystem path will a copy of Perfetto. If provided, magic-trace will
(* Points to a filesystem path with a copy of Perfetto. If provided, magic-trace will
automatically start a local HTTP server for you to view the trace. You can use this
"hidden" feature to serve a local copy of Perfetto if you don't want to copy trace
files around. *)
Expand All @@ -22,3 +22,8 @@ let debug = Option.is_some (Unix.getenv "MAGIC_TRACE_DEBUG")
[--dlfilter], this environment variable allows the user to forcibly disable
filtering. *)
let no_dlfilter = Option.is_some (Unix.getenv "MAGIC_TRACE_NO_DLFILTER")

(* Points to a filesystem path with a trace processor executable.
If provided, the flag can be indicated to automatically run the processor
at the path once the trace file is created *)
let processor_path = Unix.getenv "MAGIC_TRACE_PROCESSOR_SHELL_PATH"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let processor_path = Unix.getenv "MAGIC_TRACE_PROCESSOR_SHELL_PATH"
let processor_path = Unix.getenv "MAGIC_TRACE_TRACE_PROCESSOR_SHELL_PATH"

1 change: 1 addition & 0 deletions src/env_vars.mli
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ val debug : bool
val perf_is_privileged : bool
val perfetto_dir : string option
val no_dlfilter : bool
val processor_path : string option
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
val processor_path : string option
val trace_processor_shell_path : string option

to be explicit

112 changes: 87 additions & 25 deletions src/trace.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ let supports_command command =
Lazy.from_fun (fun () ->
match Core_unix.fork () with
| `In_the_child ->
Core_unix.close Core_unix.stdout;
Core_unix.close Core_unix.stderr;
(* gracefully hide perf outputs *)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should leave a comment here about what versions of perf we observed breakages when close-ing and under what circumstances, because it was surprising.

let devnull = Core_unix.openfile ~mode:[ O_WRONLY ] "/dev/null" in
Core_unix.dup2 ~src:devnull ~dst:Core_unix.stdout ();
Core_unix.dup2 ~src:devnull ~dst:Core_unix.stderr ();
Core_unix.exec ~prog:command ~argv:[ command; "--version" ] ~use_path:true ()
|> never_returns
| `In_the_parent pid ->
Expand Down Expand Up @@ -561,6 +563,41 @@ module Make_commands (Backend : Backend_intf.S) = struct
{ Decode_opts.output_config; decode_opts; print_events }
;;

module Enable = struct
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
module Enable = struct
module Enable = struct

Nit: we should give this module a more descriptive name.

type enabled = { value : string }

type t =
| Disabled
| Enabled of enabled
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| Enabled of enabled
| Enabled of { value : string }

Let's inline this type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also give value a better name, like trace_processor_shell_path.


let processor_param =
match Env_vars.processor_path with
| None -> Command.Param.return Disabled
| Some processor_shell_path ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| Some processor_shell_path ->
| Some trace_processor_shell_path ->

Nit: avoid having multiple variable names for the semantically-same thing. It's best to shadow the previously-declared name in such cases.

let%map_open.Command processor =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let%map_open.Command processor =
let%map_open.Command use_trace_processor_shell =

Nit: descriptive variable names.

flag
"use-trace-processor-shell"
no_arg
~doc:[%string "use the trace processor set in environment variables"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
~doc:[%string "use the trace processor set in environment variables"]
~doc:[%string "use trace processor shell to view trace"]

in
if processor then Enabled { value = processor_shell_path } else Disabled
;;
end

(* Same as [Caml.exit] but does not run at_exit handlers *)
external sys_exit : int -> 'a = "caml_sys_exit"

let call_trace_processor ?env ~prog ~argv () =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's either generalize this with the perf version, or leave a CR-someday to do this later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure which part of this function is perf-specific.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of it is, so ideally this'd be the same function as

let perf_fork_exec ?env ~prog ~argv () =
so we don't have two copies.

You don't need to do this in this PR, but it would be good to leave a note for the future.

let pr_set_pdeathsig = Or_error.ok_exn Linux_ext.pr_set_pdeathsig in
match Core_unix.fork () with
| `In_the_child ->
pr_set_pdeathsig Signal.kill;
never_returns
(try Core_unix.exec ?env ~prog ~argv () with
| _ -> sys_exit 127)
| `In_the_parent _ -> ()
;;

let run_command =
Command.async_or_error
~summary:"Runs a command and traces it."
Expand All @@ -573,11 +610,14 @@ module Make_commands (Backend : Backend_intf.S) = struct
magic-trace run -multi-thread ./program -- arg1 arg2\n\n\
# Run a process, tracing its entire execution (only practical for short-lived \
processes)\n\
magic-trace run -full-execution ./program\n")
magic-trace run -full-execution ./program\n\
# Run a process that generates a large trace file, magic-trace run ./program \
-use-trace-processor-shell\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should leave this out of the documentation for now, or maybe make the -use-trace-processor-shell part of it conditional on the environment variable being present. Definitely don't want to give help text for a flag that doesn't exist.

(let%map_open.Command record_opt_fn = record_flags
and decode_opts = decode_flags
and debug_print_perf_commands = debug_print_perf_commands
and prog = anon ("COMMAND" %: string)
and trace_processor_exe = Enable.processor_param
and argv =
flag "--" escape ~doc:"ARGS Arguments for the command. Ignored by magic-trace."
in
Expand All @@ -589,31 +629,53 @@ module Make_commands (Backend : Backend_intf.S) = struct
| Some path -> path
| None -> failwithf "Can't find executable for %s" prog ()
in
record_opt_fn ~executable ~f:(fun opts ->
let elf = Elf.create opts.executable in
let%bind range_symbols =
evaluate_trace_filter ~trace_filter:opts.trace_filter ~elf
in
let%bind pid =
let argv = prog :: List.concat (Option.to_list argv) in
run_and_record
opts
let%bind () =
record_opt_fn ~executable ~f:(fun opts ->
let elf = Elf.create opts.executable in
let%bind range_symbols =
evaluate_trace_filter ~trace_filter:opts.trace_filter ~elf
in
let%bind pid =
let argv = prog :: List.concat (Option.to_list argv) in
run_and_record
opts
~elf
~debug_print_perf_commands
~prog
~argv
~collection_mode:opts.collection_mode
in
let%bind.Deferred perf_maps = Perf_map.Table.load_by_pids [ pid ] in
decode_to_trace
~perf_maps
?range_symbols
~elf
~trace_scope:opts.trace_scope
~debug_print_perf_commands
~prog
~argv
~record_dir:opts.record_dir
~collection_mode:opts.collection_mode
in
let%bind.Deferred perf_maps = Perf_map.Table.load_by_pids [ pid ] in
decode_to_trace
~perf_maps
?range_symbols
~elf
~trace_scope:opts.trace_scope
~debug_print_perf_commands
~record_dir:opts.record_dir
~collection_mode:opts.collection_mode
decode_opts))
decode_opts)
in
let output_config = decode_opts.Decode_opts.output_config in
let output = Tracing_tool_output.output output_config in
let output_file =
match output with
| `Sexp _ -> failwith "unimplemented"
| `Fuchsia store_path -> store_path (* path for tracing output file *)
in
let%bind.Deferred () =
match trace_processor_exe with
| Disabled ->
print_endline "Warning: must use local processor on large trace files";
Deferred.return ()
| Enabled processor_path ->
Deferred.return
(call_trace_processor
~prog:processor_path.value
~argv:[ "-D"; output_file ]
())
in
return ())
;;

let select_pid () =
Expand Down
1 change: 1 addition & 0 deletions src/tracing_tool_output.ml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ type t =
{ serve : Serve.t
; output : [ `Fuchsia of string | `Sexp of string ]
}
[@@deriving fields]

let store_path = function
| `Fuchsia store_path | `Sexp store_path -> store_path
Expand Down
2 changes: 2 additions & 0 deletions src/tracing_tool_output.mli
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ open! Async

type t

val output : t -> [ `Fuchsia of string | `Sexp of string ]

(** Offers configuration parameters for where to save a file and whether to serve it *)
val param : t Command.Param.t

Expand Down