Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
4 changes: 4 additions & 0 deletions src/event.ml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ module Ok = struct
; count : int
; name : Collection_mode.Event.Name.t
}
| Ptwrite of
{ location : Location.t
; data : string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is data of type string? The ptwrite instruction can only hold a 32-bit/64-bit payload, and judging by perf_ptwrite_event_re in src/perf_decode.ml it appears perf script renders it as a number.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't see any particular reason, fixed

}
[@@deriving sexp, bin_io]
end

Expand Down
4 changes: 4 additions & 0 deletions src/event.mli
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ module Ok : sig
; count : int
; name : Collection_mode.Event.Name.t
} (** Represents counter based events collected through sampling. *)
| Ptwrite of
{ location : Location.t
; data : string
} (** Represents ptwrite events collected through Intel PT using PTWRITE. *)
[@@deriving sexp]
end

Expand Down
6 changes: 5 additions & 1 deletion src/for_range.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ let range_hit_times ~decode_events ~range_symbols =
let is_start symbol = String.(Symbol.display_name symbol = start_symbol) in
let is_stop symbol = String.(Symbol.display_name symbol = stop_symbol) in
Pipe.filter_map events ~f:(function
| Error _ | Ok { data = Power _; _ } | Ok { data = Event_sample _; _ } -> None
| Error _
| Ok { data = Power _; _ }
| Ok { data = Event_sample _; _ }
| Ok { data = Ptwrite _; _ } -> None
| Ok { data = Trace trace; time; _ } ->
(match trace.kind with
| Some Call ->
Expand Down Expand Up @@ -93,6 +96,7 @@ let decode_events_and_annotate ~decode_events ~range_symbols =
| Ok { data = Power _; _ }
| Ok { data = Stacktrace_sample _; _ }
| Ok { data = Event_sample _; _ }
| Ok { data = Ptwrite _; _ }
| Error _ -> hits, in_filtered_region)
in
( (hits, in_filtered_region)
Expand Down
77 changes: 70 additions & 7 deletions src/new_trace_writer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,26 @@ module Thread_info = struct
Trace_segment.add_event trace_segment event_data (Timestamp.create time)
;;

let set_fiber_id t fiber_id =
let #(trace_segment, ~in_filtered_region:_) = Nonempty_vec.last t.trace_segments in
Trace_segment.set_fiber_id trace_segment fiber_id
;;

module New_trace_segment_kind = struct
type t =
| Independent
| Continuing_from_current
end

let start_new_trace_segment t ~in_filtered_region ~(kind : New_trace_segment_kind.t) =
let start_new_trace_segment
t
~in_filtered_region
~(kind : New_trace_segment_kind.t)
~fiber_stacks
=
let new_trace_segment =
match kind with
| Independent -> Trace_segment.create t.ocaml_exception_info
| Independent -> Trace_segment.create t.ocaml_exception_info fiber_stacks
| Continuing_from_current ->
let #(current, ~in_filtered_region:_) = Nonempty_vec.last t.trace_segments in
Trace_segment.create_continuing_from current
Expand All @@ -89,6 +99,8 @@ type 'thread inner =
; annotate_inferred_start_times : bool
; mutable in_filtered_region : bool
; mutable transaction_events : Event.With_write_info.t Deque.t
; pending_flows : ('thread -> Time_ns.Span.t -> unit) Hashtbl.M(Int).t
; fiber_stacks : Trace_segment.Fiber.t Hashtbl.M(Int).t
}

type t = T : 'thread inner -> t
Expand Down Expand Up @@ -230,6 +242,8 @@ let create_expert
; annotate_inferred_start_times
; in_filtered_region = true
; transaction_events = Deque.create ()
; pending_flows = Hashtbl.create (module Int)
; fiber_stacks = Hashtbl.create (module Int)
}
in
write_hits t hits;
Expand Down Expand Up @@ -336,7 +350,7 @@ let create_thread t event =
; extra_event_tracks = Hashtbl.create (module Collection_mode.Event.Name)
; trace_segments =
Nonempty_vec.create
#( Trace_segment.create t.ocaml_exception_info
#( Trace_segment.create t.ocaml_exception_info t.fiber_stacks
, ~in_filtered_region:t.in_filtered_region )
}
;;
Expand Down Expand Up @@ -379,7 +393,8 @@ let maybe_start_filtered_region t ~should_write ~time:_ =
Thread_info.start_new_trace_segment
thread_info
~in_filtered_region:true
~kind:Continuing_from_current);
~kind:Continuing_from_current
~fiber_stacks:t.fiber_stacks);
t.in_filtered_region <- true)
;;

Expand All @@ -392,7 +407,8 @@ let maybe_stop_filtered_region t ~should_write =
Thread_info.start_new_trace_segment
thread_info
~in_filtered_region:false
~kind:Continuing_from_current))
~kind:Continuing_from_current
~fiber_stacks:t.fiber_stacks))
;;

let write_event_and_callstack (events_writer : Tracing_tool_output.events_writer) event =
Expand Down Expand Up @@ -501,6 +517,7 @@ and write_event' (T t) ?events_writer event =
thread_info
~in_filtered_region:t.in_filtered_region
~kind:Independent
~fiber_stacks:t.fiber_stacks
| Ok event_value ->
if should_write
then
Expand Down Expand Up @@ -532,8 +549,8 @@ and write_event' (T t) ?events_writer event =
~f:(fun pid -> [ "pid", Int (Pid.to_int pid) ])
~default:[]
; Option.value_map
(Event.thread outer_event).pid
~f:(fun pid -> [ "tid", Int (Pid.to_int pid) ])
(Event.thread outer_event).tid
~f:(fun tid -> [ "tid", Int (Pid.to_int tid) ])
~default:[]
])
in
Expand All @@ -555,6 +572,52 @@ and write_event' (T t) ?events_writer event =
~name:"CPU"
~time
~args:Tracing.Trace.Arg.[ "freq (MHz)", Int freq ]
| { Event.Ok.thread = _ (* Already used this to look up thread info. *)
; time = _
; data = Ptwrite { location; data }
; in_transaction = _
} ->
let symbol_name = Symbol.display_name location.symbol in
let is_perform = String.equal symbol_name "caml_perform" in
let is_resume = String.equal symbol_name "caml_resume" in
if is_perform || is_resume
then (
let fiber_id = Int64.to_int_trunc (Int64.of_string data) in
Thread_info.set_fiber_id thread_info fiber_id;
let name = if is_perform then "Perform Effect" else "Resume Continuation" in
let args = Tracing.Trace.Arg.[ "fiber", String (sprintf "0x%x" fiber_id) ] in
write_duration_complete t ~thread ~args ~name ~time ~time_end:time;
if is_perform
then (
let module T = (val t.trace) in
let flow = T.create_flow () in
T.write_flow_step flow ~thread ~time:(time :> Time_ns.Span.t);
Hashtbl.set t.pending_flows ~key:fiber_id ~data:(fun thread time ->
T.write_flow_step flow ~thread ~time;
T.finish_flow flow))
else (
match Hashtbl.find_and_remove t.pending_flows fiber_id with
| Some finish -> finish thread (time :> Time_ns.Span.t)
| None -> ()))
else (
let args =
Tracing.Trace.Arg.(
List.concat
[ [ "timestamp", Int (Time_ns.Span.to_int_ns (time :> Time_ns.Span.t)) ]
; [ "symbol", String symbol_name ]
; [ "addr", Pointer location.instruction_pointer ]
; [ "data", String data ]
; Option.value_map
(Event.thread outer_event).pid
~f:(fun pid -> [ "pid", Int (Pid.to_int pid) ])
~default:[]
; Option.value_map
(Event.thread outer_event).tid
~f:(fun tid -> [ "tid", Int (Pid.to_int tid) ])
~default:[]
])
in
write_duration_complete t ~thread ~args ~name:"PTWRITE" ~time ~time_end:time)
| { Event.Ok.data = Stacktrace_sample _; _ } ->
(* This should be unreachable, we currently delegate support for sampling to the old trace-writer. *)
assert false)
Expand Down
15 changes: 15 additions & 0 deletions src/perf_capabilities.ml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ let snapshot_on_exit = bit 3
let last_branch_record = bit 4
let dlfilter = bit 5
let ctlfd = bit 6
let ptwrite = bit 7

include Flags.Make (struct
let allow_intersecting = false
Expand All @@ -22,6 +23,7 @@ include Flags.Make (struct
; last_branch_record, "last_branch_record"
; dlfilter, "dlfilter"
; ctlfd, "ctlfd"
; ptwrite, "ptwrite"
]
;;
end)
Expand Down Expand Up @@ -58,6 +60,18 @@ let supports_configurable_psb_period () =
| Sys_error _ -> false
;;

let supports_ptwrite () =
try
let ptwrite_cap =
In_channel.read_all "/sys/bus/event_source/devices/intel_pt/caps/ptwrite"
in
String.( = ) ptwrite_cap "1\n"
with
(* Even if this file is not present (i.e. when Intel PT isn't present), we
don't want capability checking to fail. *)
| Sys_error _ -> false
;;

(* This checks if pdcm flag is present in /proc/cpuinfo. This is necessary for
LBR to work. Although I couldn't ascertain that it is also sufficient.
However it seems unlikely this would fail on most machines. *)
Expand Down Expand Up @@ -113,6 +127,7 @@ let detect_exn () =
let set_if bool flag cap = cap + if bool then flag else empty in
empty
|> set_if (supports_configurable_psb_period ()) configurable_psb_period
|> set_if (supports_ptwrite ()) ptwrite
|> set_if (supports_tracing_kernel ()) kernel_tracing
|> set_if (supports_kcore version) kcore
|> set_if (supports_snapshot_on_exit version) snapshot_on_exit
Expand Down
1 change: 1 addition & 0 deletions src/perf_capabilities.mli
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ val snapshot_on_exit : t
val last_branch_record : t
val dlfilter : t
val ctlfd : t
val ptwrite : t
val detect_exn : unit -> t Deferred.t
100 changes: 98 additions & 2 deletions src/perf_decode.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ let perf_cbr_event_re =
Re.Perl.re {|^ *([a-z )(]*)? +cbr: +([0-9]+ +freq: +([0-9]+) MHz)?(.*)$|} |> Re.compile
;;

let perf_ptwrite_event_re =
Re.Perl.re
{|^ *(call|return|tr strt|syscall|sysret|hw int|iret|int|tx abrt|tr end|tr strt tr end|tr end (?:async|call|return|syscall|sysret|iret)|jmp|jcc)? +IP: +([0-9a-f]+) +payload: +((?:0x)?[0-9a-f]+) (.*) ([0-9]+) +([0-9a-f]+) (.*)$|}
|> Re.compile
;;

let trace_error_re =
Re.Posix.re
{|^ instruction trace error type [0-9]+ (time ([0-9]+)\.([0-9]+) )?cpu [\-0-9]+ pid ([\-0-9]+) tid ([\-0-9]+) ip (0x[0-9a-fA-F]+|0) code [0-9]+: (.*)$|}
Expand All @@ -47,7 +53,15 @@ type header =
{ thread : Event.Thread.t
; time : Time_ns.Span.t
; period : int
; event : [ `Branches | `Cbr | `Psb | `Cycles | `Branch_misses | `Cache_misses ]
; event :
[ `Branches
| `Cbr
| `Psb
| `Cycles
| `Branch_misses
| `Cache_misses
| `Ptwrite
]
; remaining_line : string
}

Expand Down Expand Up @@ -99,6 +113,7 @@ let parse_event_header line =
| "cycles" -> `Cycles
| "branch-misses" -> `Branch_misses
| "cache-misses" -> `Cache_misses
| "ptwrite" -> `Ptwrite
| _ ->
raise_s
[%message
Expand Down Expand Up @@ -349,6 +364,33 @@ let parse_perf_extra_sampled_event
}
;;

let parse_perf_ptwrite_event ?perf_maps (thread : Event.Thread.t) time line : Event.t =
match Re.Group.all (Re.exec perf_ptwrite_event_re line) with
| [| _
; _branch_kind
; _fup_ip
; payload
; _payload_ascii
; _unknown
; instruction_pointer
; symbol_and_offset
|] ->
let location =
parse_location ?perf_maps ~pid:thread.pid instruction_pointer symbol_and_offset
in
Ok
{ thread
; time
; data = Ptwrite { location; data = payload }
; in_transaction = false
}
| results ->
raise_s
[%message
"Regex of perf ptwrite event did not match expected fields"
(results : string array)]
;;

let to_event ?perf_maps lines : Event.t option =
try
match lines with
Expand Down Expand Up @@ -385,7 +427,9 @@ let to_event ?perf_maps lines : Event.t option =
period
remaining_line
lines
Cache_misses)))
Cache_misses)
| `Ptwrite ->
Some (parse_perf_ptwrite_event ?perf_maps thread time remaining_line)))
with
| exn ->
raise_s
Expand Down Expand Up @@ -755,6 +799,58 @@ module%test _ = struct
(Trace (trace_state_change End) (kind Async) (src 0x7f6fce0b71f4)
(dst 0x0)))))) |}]
;;

let%expect_test "perf ptwrite event" =
check
{| 2769074/2769293 2592496.569782106: 1 ptwrite: call IP: 0 payload: 0x1b5 0 55c7952ad2d0 [unknown] (foo.bin)|};
[%expect
{|
((Ok
((thread ((pid (2769074)) (tid (2769293)))) (time 30d8m16.569782106s)
(data (Ptwrite (location 0x55c7952ad2d0) (data 0x1b5)))))) |}]
;;

let%expect_test "perf ptwrite event with printable payload" =
check
{|2817453/2817483 2596547.813541321: 1 ptwrite: call IP: 0 payload: 0x62 b 0 613d946b42d0 [unknown] (foo.bin)|};
[%expect
{|
((Ok
((thread ((pid (2817453)) (tid (2817483)))) (time 30d1h15m47.813541321s)
(data (Ptwrite (location 0x613d946b42d0) (data 0x62)))))) |}]
;;

let%expect_test "perf ptwrite event with jcc instruction" =
check
{|3256341/3256760 2632746.069047397: 1 ptwrite: jcc IP: 0 payload: 0x5000000000061b2 0 577bcad80555 [unknown] (foo.bin)|};
[%expect
{|
((Ok
((thread ((pid (3256341)) (tid (3256760)))) (time 30d11h19m6.069047397s)
(data (Ptwrite (location 0x577bcad80555) (data 0x5000000000061b2)))))) |}]
;;

let%expect_test "perf ptwrite event without instruction" =
check
{|3285832/3286108 2634666.172476558: 1 ptwrite: IP: 0 payload: 0xa00000000000000 0 64d20fb10432 [unknown] (foo.bin)|};
[%expect
{|
((Ok
((thread ((pid (3285832)) (tid (3286108)))) (time 30d11h51m6.172476558s)
(data (Ptwrite (location 0x64d20fb10432) (data 0xa00000000000000))))))
|}]
;;

let%expect_test "perf ptwrite event zero payload" =
check
{|997136/997136 340026.788189283: 1 ptwrite: jcc IP: 0 payload: 0 0 64d20fb10432 [unknown] (foo.bin)|};
[%expect
{|
((Ok
((thread ((pid (997136)) (tid (997136)))) (time 3d22h27m6.788189283s)
(data (Ptwrite (location 0x64d20fb10432) (data 0))))))
|}]
;;
end

module For_testing = struct
Expand Down
Loading