open! Core open! Import module Generated_interop = struct (*$ open Magic_trace_lib_cinaps_helpers.Trace_decoding_interop;; gen_ocaml_enum event_kinds;; gen_ocaml_enum decode_result;; gen_ocaml_record event_packet;; gen_ocaml_record add_section_packet;; gen_ocaml_record decoding_config *) module Event_kind = struct type t = | Other | Call | Ret | Start_trace | End_trace | End_trace_syscall | Install_handler | Raise_exception | Decode_error | Jump [@@deriving sexp] end module Decode_result = struct type t = | End_of_trace | Event | Add_section [@@deriving sexp] end module Event = struct type t = { mutable pid : int ; mutable tid : int ; mutable kind : Event_kind.t ; mutable addr : int ; mutable time : int ; mutable isid : int ; mutable symbol_begin : int ; mutable symbol_end : int } [@@deriving sexp] end module Add_section = struct type t = { mutable filename : string ; mutable offset : int ; mutable size : int ; mutable vaddr : int ; mutable isid : int } [@@deriving sexp] end module Config = struct type t = { mutable sideband_filename : string ; mutable pt_data_fd : int ; mutable setup_info : Manual_perf.Setup_info.t } [@@deriving sexp] end (*$*) end module Stub = struct include Generated_interop (* In event, [symbol_{begin,end}] are set by OCaml and used by C to check if a jump remained in the same symbol, so we don't have to look up symbols after every jump. *) let blank_event () : Event.t = { pid = 1 ; tid = 1 ; kind = Event_kind.Other ; addr = 0 ; time = 0 ; isid = 0 ; symbol_begin = 0 ; symbol_end = 0 } ;; let blank_add_section () : Add_section.t = { filename = ""; offset = 0; size = 0; vaddr = 0; isid = 0 } ;; type c_decoding_state external init_decoder : Config.t -> c_decoding_state = "magic_pt_init_decoder_stub" external run_decoder : c_decoding_state -> Event.t -> Add_section.t -> Decode_result.t = "magic_pt_run_decoder_stub" end type state = { elfs_by_filename : Elf.t Filename.Table.t ; resolver_by_isid : Elf.Symbol_resolver.t Int.Table.t ; mutable fallback_resolvers : (int * int * Elf.Symbol_resolver.t) list } let handle_add_section (state : state) (add : Stub.Add_section.t) = (* Core.print_s [%message "handle_add_section" (add : Stub.Add_section.t)]; *) let elf = match Hashtbl.find state.elfs_by_filename add.filename with | Some elf -> Some elf | None -> (match Elf.create add.filename with | None -> None | Some elf -> Hashtbl.add_exn state.elfs_by_filename ~key:add.filename ~data:elf; Some elf) in match elf with | None -> () | Some elf -> let resolver : Elf.Symbol_resolver.t = { elf; file_offset = add.offset; loaded_offset = add.vaddr } in state.fallback_resolvers <- (add.vaddr, add.size, resolver) :: state.fallback_resolvers; Hashtbl.add_exn state.resolver_by_isid ~key:add.isid ~data:resolver ;; let convert_trace_event state (event : Stub.Event.t) = let resolved : Elf.Symbol_resolver.resolved = let resolver = match Hashtbl.find state.resolver_by_isid event.isid with | Some resolver -> Some resolver | None -> (* Tracing start/stop events don't have an isid so we need a fallback *) List.find state.fallback_resolvers ~f:(fun (vaddr, size, _) -> event.addr > vaddr && event.addr < vaddr + size) |> Option.map ~f:(fun (_, _, r) -> r) in match resolver with | None -> { name = "[unknown section]"; start_addr = 0; end_addr = 0 } | Some resolver -> Elf.Symbol_resolver.resolve resolver event.addr |> Option.value ~default:{ name = "[unknown symbol]"; start_addr = 0; end_addr = 0 } in event.symbol_begin <- resolved.start_addr; event.symbol_end <- resolved.end_addr; let symbol = resolved.name in (* print_s [%message (event : Stub.Event.t) (symbol : string)]; *) let thread : Backend_intf.Event.Thread.t = { pid = Pid.of_int event.pid; tid = event.tid } in let time = Time_ns.Span.of_int_ns event.time in let addr = event.addr in let kind = match event.kind with | Generated_interop.Event_kind.Other -> Backend_intf.Event.Unknown | Call -> Call | Ret -> Return | Start_trace -> Start | End_trace -> End None | End_trace_syscall -> End Syscall | Install_handler -> Other "Install_handler" | Raise_exception -> Other "Raise_exception" | Jump -> Jump | Decode_error -> Decode_error in let offset = event.addr - resolved.start_addr in { Backend_intf.Event.thread ; time ; addr ; kind ; symbol ; offset ; ip ; ip_symbol ; ip_offset } ;; type t = { state : state ; decoder : Stub.c_decoding_state ; event : Stub.Event.t ; add_section : Stub.Add_section.t } let create ~pt_file ~sideband_file ~setup_file = let setup_info = In_channel.read_all setup_file |> Sexp.of_string |> [%of_sexp: Manual_perf.Setup_info.t] in (* print_s [%sexp (setup_info : Manual_perf.Setup_info.t)]; *) let%bind.With pt_data_fd f = Core_unix.with_file pt_file ~mode:[ O_RDONLY ] ~f in let config : Stub.Config.t = { sideband_filename = sideband_file ; pt_data_fd = Core_unix.File_descr.to_int pt_data_fd ; setup_info } in let state = { elfs_by_filename = Filename.Table.create () ; resolver_by_isid = Int.Table.create () ; fallback_resolvers = [] } in let event = Stub.blank_event () in let add_section = Stub.blank_add_section () in let decoder = Stub.init_decoder config in { state; decoder; event; add_section } ;; let rec decode_one t = match Stub.run_decoder t.decoder t.event t.add_section with | End_of_trace -> None | Event -> Some (convert_trace_event t.state t.event) | Add_section -> handle_add_section t.state t.add_section; decode_one t ;;