2020type slot = Printexc .backtrace_slot
2121
2222(* What's sent via runtime events. this HAS to be marshalable*)
23- type raw_stack_trace = {slots : slot array ; domain_id : int ; thread_name : string }
23+ type raw_stack_trace = {
24+ slots : slot array ;
25+ domain_id : int ;
26+ thread_name : string ;
27+ }
2428
2529let raw_stack_trace_of_backtrace bt : raw_stack_trace =
2630 (* Use the domain as the ID since runtime event sampling happens per domain *)
@@ -30,7 +34,7 @@ let raw_stack_trace_of_backtrace bt : raw_stack_trace =
3034 let name = if Domain. is_main_domain () then " main" else string_of_int did in
3135 (* if there aren't any slots then not much we can do *)
3236 let slots = Option. value ~default: [||] Printexc. (backtrace_slots bt) in
33- {slots; domain_id= did; thread_name= name}
37+ { slots; domain_id = did; thread_name = name }
3438
3539(* ****************************************************************************)
3640(* Stack frames *)
@@ -39,39 +43,65 @@ let raw_stack_trace_of_backtrace bt : raw_stack_trace =
3943(* Inlined functions are filtered out in ocaml_intf always right now, but we
4044 probably want to give that as an option at some point *)
4145(* coupling: ocaml_intf *)
42- type frame = {name : string ; filename : string ; line : int ; inlined : bool }
46+ type frame = {
47+ name : string ;
48+ filename : string ;
49+ line : int ;
50+ inlined : bool ; [@ eq.skip]
51+ (* We really don't care about if a function is inlined for equality*)
52+ }
53+ [@@ deriving eq ]
4354
4455let stack_frame_of_slot (slot : Printexc.backtrace_slot ) : frame option =
4556 let loc = Printexc.Slot. location slot in
4657 let name = Printexc.Slot. name slot in
4758 let inlined = Printexc.Slot. is_inline slot in
4859 match (loc, name) with
4960 | Some loc , Some name ->
50- Some {name; filename= loc.filename; line= loc.line_number; inlined}
51- | None , Some name ->
52- Some {name; filename= " <unknown>" ; line= 0 ; inlined}
61+ Some { name; filename = loc.filename; line = loc.line_number; inlined }
62+ | None , Some name -> Some { name; filename = " <unknown>" ; line = 0 ; inlined }
5363 | Some loc , None ->
5464 Some
55- { name= " <unknown>"
56- ; filename= loc.filename
57- ; line= loc.line_number
58- ; inlined }
59- | None , None ->
60- None
65+ {
66+ name = " <unknown>" ;
67+ filename = loc.filename;
68+ line = loc.line_number;
69+ inlined;
70+ }
71+ | None , None -> None
72+
73+ (* Looking ahead by up to 3 can be useful for recursive functions to make them
74+ much more legible. E.g. List.map can be very recursive, and pyroscope has a
75+ ~1000 stack frame limit, so if we iterate through say, 10k items there's a
76+ good chance we may max out, which will cause any frames past the limit to be
77+ dropped and instead replaced with a single frame that says "other"*)
78+ let compress frames =
79+ let rec aux acc = function
80+ | [] -> List. rev acc
81+ | f1 :: f2 :: f3 :: (f4 :: f5 :: f6 :: _ as rest)
82+ when equal_frame f1 f4 && equal_frame f2 f5 && equal_frame f3 f6 ->
83+ aux acc rest
84+ | f1 :: f2 :: (f3 :: f4 :: _ as rest)
85+ when equal_frame f1 f3 && equal_frame f2 f4 ->
86+ aux acc rest
87+ | f1 :: (f2 :: _ as rest ) when equal_frame f1 f2 -> aux acc rest
88+ | f :: rest -> aux (f :: acc) rest
89+ in
90+ aux [] frames
6191
6292let stack_frames_of_slots slots =
63- slots
64- |> List. map (fun slot -> slot |> stack_frame_of_slot)
65- |> List. filter_map Fun. id
93+ slots |> List. filter_map stack_frame_of_slot |> compress
6694
6795(* ****************************************************************************)
6896(* Stack traces *)
6997(* ****************************************************************************)
7098(* coupling: ocaml_intf *)
71- type t = {frames : frame list ; thread_id : int ; thread_name : string }
99+ type t = { frames : frame list ; thread_id : int ; thread_name : string }
72100
73101let t_of_raw_stack_trace raw_stack_trace =
74102 let frames = stack_frames_of_slots (Array. to_list raw_stack_trace.slots) in
75- { frames
76- ; thread_id= raw_stack_trace.domain_id
77- ; thread_name= raw_stack_trace.thread_name }
103+ {
104+ frames;
105+ thread_id = raw_stack_trace.domain_id;
106+ thread_name = raw_stack_trace.thread_name;
107+ }
0 commit comments