From 7b7a0d2de82403aeff6e36a3f62703bd030ce7fd Mon Sep 17 00:00:00 2001 From: Kevin-Li-2025 <2242139@qq.com> Date: Wed, 24 Jun 2026 14:06:24 +0100 Subject: [PATCH] Support running commands while attached Signed-off-by: Kevin-Li-2025 <2242139@qq.com> --- README.md | 2 +- src/trace.ml | 85 ++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 74 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 832a55e44..497abb9fd 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ You can point magic-trace at a function such that when your application calls it 1. [Here](https://raw.githubusercontent.com/janestreet/magic-trace/master/demo/demo.c)'s a sample C program to try out. It's a slightly modified version of the example in `man 3 dlopen`. Download that, build it with `gcc demo.c -ldl -o demo`, then leave it running `./demo`. We're going to use that program to learn how `dlopen` works. -2. Run `magic-trace attach -pid $(pidof demo)`. When you see the message that it's successfully attached, wait a couple seconds and Ctrl+C `magic-trace`. It will output a file called `trace.fxt.gz` in your working directory. +2. Run `magic-trace attach -pid $(pidof demo)`. When you see the message that it's successfully attached, wait a couple seconds and Ctrl+C `magic-trace`. It will output a file called `trace.fxt.gz` in your working directory. If you want to trace while running a specific workload, pass the workload after `--`; for example, `magic-trace attach -pid $(pidof demo) -- ./load-generator`.

diff --git a/src/trace.ml b/src/trace.ml index 4d0004703..38d4adb88 100644 --- a/src/trace.ml +++ b/src/trace.ml @@ -538,6 +538,46 @@ module Make_commands (Backend : Backend_intf.S) = struct detach attachment ;; + let attach_and_record_while_running_command + record_opts + ~elf + ~debug_print_perf_commands + ~collection_mode + pids + ~prog + ~argv + = + let%bind.Deferred.Or_error attachment = + attach + record_opts + ~elf + ~debug_print_perf_commands + ~subcommand:Attach + ~collection_mode + pids + in + let { Attachment.done_ivar; _ } = attachment in + let stop = Ivar.read done_ivar in + Async_unix.Signal.handle ~stop [ Signal.int ] ~f:(fun (_ : Signal.t) -> + Core.eprintf "[ Got signal, detaching... ]\n%!"; + Ivar.fill_if_empty done_ivar ()); + Deferred.upon stop (fun () -> Core.Signal.Expert.set Signal.int Default); + Core.eprintf "[ Attached. Running command... ]\n%!"; + let command_finished = Process.run_forwarding ~prog ~args:argv () in + let command_result = Ivar.create () in + Deferred.upon command_finished (fun result -> + Core.eprintf "[ Command exited, detaching... ]\n%!"; + Ivar.fill_if_empty done_ivar (); + Ivar.fill_if_empty command_result (Some result)); + Deferred.upon stop (fun () -> Ivar.fill_if_empty command_result None); + let%bind.Deferred command_result = Ivar.read command_result in + let%bind () = stop in + let%bind () = detach attachment in + match command_result with + | None -> Deferred.Or_error.return () + | Some result -> Deferred.return result + ;; + let record_dir_flag mode = let open Command.Param in flag @@ -605,6 +645,12 @@ module Make_commands (Backend : Backend_intf.S) = struct { Decode_opts.output_config; decode_opts; print_events } ;; + let command_argv_param ~escape_doc = + let%map_open.Command command = anon (maybe ("COMMAND" %: string)) + and more_command = flag "--" escape ~doc:escape_doc in + Option.to_list command @ Option.value more_command ~default:[] + ;; + let run_command = Command.async_or_error ~summary:"Runs a command and traces it." @@ -622,11 +668,8 @@ module Make_commands (Backend : Backend_intf.S) = struct and decode_opts = decode_flags and debug_print_perf_commands and argv = - let%map_open.Command command = anon (maybe ("COMMAND" %: string)) - and more_command = - flag "--" escape ~doc:"ARGS Arguments for the command. Ignored by magic-trace." - in - Option.to_list command @ Option.value more_command ~default:[] + command_argv_param + ~escape_doc:"ARGS Arguments for the command. Ignored by magic-trace." in fun () -> let open Deferred.Or_error.Let_syntax in @@ -719,7 +762,9 @@ module Make_commands (Backend : Backend_intf.S) = struct magic-trace attach\n\n\ # Fuzzy-find to select a running process and symbol to trigger on, snapshotting \ the next time the symbol is called\n\ - magic-trace attach -trigger ?\n") + magic-trace attach -trigger ?\n\n\ + # Attach to a process, run a command, then detach when the command exits\n\ + magic-trace attach -p $PID -- ./load-generator arg1 arg2\n") (let%map_open.Command record_opt_fn = record_flags and decode_opts = decode_flags and debug_print_perf_commands @@ -731,6 +776,11 @@ module Make_commands (Backend : Backend_intf.S) = struct ~doc: "PID Processes to attach to as a comma separated list. Required if you \ don't have the \"fzf\" application available in your PATH." + and command_argv = + command_argv_param + ~escape_doc: + "COMMAND Command to run while attached. magic-trace detaches after the \ + command exits." in fun () -> let open Deferred.Or_error.Let_syntax in @@ -759,12 +809,23 @@ module Make_commands (Backend : Backend_intf.S) = struct evaluate_trace_filter ~trace_filter:opts.trace_filter ~elf in let%bind () = - attach_and_record - opts - ~elf - ~debug_print_perf_commands - ~collection_mode - pids + match command_argv with + | [] -> + attach_and_record + opts + ~elf + ~debug_print_perf_commands + ~collection_mode + pids + | prog :: argv -> + attach_and_record_while_running_command + opts + ~elf + ~debug_print_perf_commands + ~collection_mode + pids + ~prog + ~argv in let%bind.Deferred perf_maps = Perf_map.Table.load_by_pids pids in decode_to_trace